Back to Developer Portal

OAuth Integration Guide

Add "Continue with Oasis" to your application in minutes.

1. Register Your App

Go to Developer Appsand create a new OAuth app. You'll receive a client_id and client_secret.

Save your client_secretimmediately — it's only shown once.

2. The Button

Use the official Oasis logo and button style so users immediately recognize the sign-in option.

Oasis logoContinue with Oasis
Oasis logo

oasis_logo.svg

Official Oasis OAuth logo · SVG · 61×51px

Download

Button guidelines

• Use the exact text "Continue with Oasis" — do not alter the wording

• Always include the Oasis logo to the left of the text

• Minimum button height: 40px · Minimum logo size: 16×14px

• Recommended style: black background (#000), white text (#fff), 8px border radius

• Do not recolor, distort, or modify the logo

HTML

<!-- Download the Oasis logo: https://oasisbio.oasiscompany.org/assets/oasis_logo.svg -->
<a href="https://oasisbio.oasiscompany.org/oauth/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI&response_type=code&scope=profile+email&state=RANDOM_STATE&code_challenge=YOUR_CODE_CHALLENGE&code_challenge_method=S256"
   style="display:inline-flex;align-items:center;gap:10px;padding:10px 20px;
          background:#000;color:#fff;border-radius:8px;text-decoration:none;
          font-family:sans-serif;font-size:15px;font-weight:500;">
  <img src="https://oasisbio.oasiscompany.org/assets/oasis_logo.svg" width="20" height="17" alt="Oasis" />
  Continue with Oasis
</a>

React / Next.js

import Image from 'next/image';

function ContinueWithOasis({ clientId, redirectUri }: { clientId: string; redirectUri: string }) {
  const handleClick = async () => {
    const verifier = generateRandomString(64);
    const challenge = base64url(await sha256(verifier));
    sessionStorage.setItem('pkce_verifier', verifier);

    const url = new URL('https://oasisbio.oasiscompany.org/oauth/authorize');
    url.searchParams.set('client_id', clientId);
    url.searchParams.set('redirect_uri', redirectUri);
    url.searchParams.set('response_type', 'code');
    url.searchParams.set('scope', 'profile email');
    url.searchParams.set('state', generateRandomString(16));
    url.searchParams.set('code_challenge', challenge);
    url.searchParams.set('code_challenge_method', 'S256');
    window.location.href = url.toString();
  };

  return (
    <button
      onClick={handleClick}
      style={{ display:'inline-flex', alignItems:'center', gap:10,
               padding:'10px 20px', background:'#000', color:'#fff',
               borderRadius:8, border:'none', cursor:'pointer',
               fontSize:15, fontWeight:500 }}
    >
      <Image src="https://oasisbio.oasiscompany.org/assets/oasis_logo.svg" width={20} height={17} alt="Oasis" />
      Continue with Oasis
    </button>
  );
}

3. Build the Authorization URL

Redirect users to this URL to start the OAuth flow:

const codeVerifier = generateRandomString(64); // store this
const codeChallenge = base64url(sha256(codeVerifier));

const authUrl = new URL('https://oasisbio.oasiscompany.org/oauth/authorize');
authUrl.searchParams.set('client_id', YOUR_CLIENT_ID);
authUrl.searchParams.set('redirect_uri', YOUR_REDIRECT_URI);
authUrl.searchParams.set('response_type', 'code');
authUrl.searchParams.set('scope', 'profile email oasisbios:read');
authUrl.searchParams.set('state', generateRandomString(16));
authUrl.searchParams.set('code_challenge', codeChallenge);
authUrl.searchParams.set('code_challenge_method', 'S256');

window.location.href = authUrl.toString();

PKCE is required. Store code_verifier in session storage.

4. Exchange Code for Tokens

After the user authorizes, they're redirected to your redirect_uri with a code parameter. Exchange it:

const response = await fetch('https://oasisbio.oasiscompany.org/api/oauth/token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: new URLSearchParams({
    grant_type: 'authorization_code',
    client_id: YOUR_CLIENT_ID,
    client_secret: YOUR_CLIENT_SECRET,
    code: CODE_FROM_REDIRECT,
    redirect_uri: YOUR_REDIRECT_URI,
    code_verifier: STORED_CODE_VERIFIER,
  }),
});

const { access_token, refresh_token, expires_in } = await response.json();

5. Access User Data

Use the access token as a Bearer token:

// Get user profile
const profile = await fetch('https://oasisbio.oasiscompany.org/api/oauth/userinfo', {
  headers: { Authorization: `Bearer ${access_token}` },
}).then(r => r.json());
// { sub, username, display_name, avatar_url, email }

// Get character list (requires oasisbios:read scope)
const characters = await fetch('https://oasisbio.oasiscompany.org/api/oauth/resources/oasisbios', {
  headers: { Authorization: `Bearer ${access_token}` },
}).then(r => r.json());

// Get DCOS documents (requires dcos:read scope)
const dcos = await fetch(`https://oasisbio.oasiscompany.org/api/oauth/resources/oasisbios/${bioId}/dcos`, {
  headers: { Authorization: `Bearer ${access_token}` },
}).then(r => r.json());

Available Scopes

profileusername, display name, avatar URL
emailemail address
oasisbios:readcharacter list (title, slug, cover image)
oasisbios:fullfull character data (abilities, worlds, eras, references)
dcos:readDCOS document content

Refreshing Tokens

Access tokens expire after 1 hour. Use the refresh token to get a new one:

const response = await fetch('https://oasisbio.oasiscompany.org/api/oauth/token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: new URLSearchParams({
    grant_type: 'refresh_token',
    client_id: YOUR_CLIENT_ID,
    client_secret: YOUR_CLIENT_SECRET,
    refresh_token: STORED_REFRESH_TOKEN,
  }),
});
// Returns new access_token and refresh_token (old one is invalidated)

OIDC Discovery

Machine-readable configuration available at: /api/oauth/.well-known/openid-configuration