The authentication flow differs based on whether you’re using a private or public application.

Private Application Flow (Authorization Code)

  1. Redirect Users to Authorization URL

    Construct the authorization URL with your client_id and callback_url:

    const authUrl = new URL("https://clerk.coloop.ai/oauth/authorize");
    authUrl.searchParams.append("client_id", "your_client_id");
    authUrl.searchParams.append("redirect_uri", "your_callback_url");
    authUrl.searchParams.append("response_type", "code");
    authUrl.searchParams.append("scope", "email profile");
    // Redirect user to authUrl
    
  2. Handle the Callback

    After authorization, CoLoop redirects to your callback URL with an authorization code:

    // Example callback URL:
    // https://your-domain.com/oauth2/callback?code=abc123...
    
  3. Exchange Code for Tokens

    Make a POST request to the token endpoint:

    const response = await fetch("https://clerk.coloop.ai/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: "authorization_code_from_callback",
        redirect_uri: "your_callback_url"
      })
    });
    const tokens = await response.json();
    // {
    //   "access_token": "...",
    //   "refresh_token": "...",
    //   "scope": "email profile",
    //   "token_type": "bearer",
    //   "expires_in": 7200
    // }
    

Public Application Flow (PKCE)

  1. Generate PKCE Challenge

    // Generate a random code verifier
    function generateCodeVerifier() {
      const array = new Uint8Array(32);
      crypto.getRandomValues(array);
      return base64UrlEncode(array);
    }
    // Create code challenge
    async function generateCodeChallenge(verifier) {
      const encoder = new TextEncoder();
      const data = encoder.encode(verifier);
      const digest = await crypto.subtle.digest("SHA-256", data);
      return base64UrlEncode(new Uint8Array(digest));
    }
    const codeVerifier = generateCodeVerifier();
    const codeChallenge = await generateCodeChallenge(codeVerifier);
    
  2. Redirect to Authorization URL with PKCE

    const authUrl = new URL("https://clerk.coloop.ai/oauth/authorize");
    authUrl.searchParams.append("client_id", "your_client_id");
    authUrl.searchParams.append("redirect_uri", "your_callback_url");
    authUrl.searchParams.append("response_type", "code");
    authUrl.searchParams.append("scope", "email profile");
    authUrl.searchParams.append("code_challenge", codeChallenge);
    authUrl.searchParams.append("code_challenge_method", "S256");
    // Redirect user to authUrl
    
  3. Exchange Code for Tokens

    const response = await fetch("https://clerk.coloop.ai/oauth/token", {
      method: "POST",
      headers: {
        "Content-Type": "application/x-www-form-urlencoded",
      },
      body: new URLSearchParams({
        grant_type: "authorization_code",
        client_id: "your_client_id",
        code: "authorization_code_from_callback",
        code_verifier: codeVerifier,
        redirect_uri: "your_callback_url"
      })
    });
    const tokens = await response.json();
    

Using the Access Token

For both flows, use the access token to make authenticated requests:

const userInfo = await fetch("https://clerk.coloop.ai/oauth/userinfo", {
  headers: {
    "Authorization": `Bearer ${access_token}`
  }
});
const user = await userInfo.json();
// {
//   "email": "user@example.com",
//   "email_verified": true,
//   "name": "John Doe",
//   ...
// }

Token Refresh

When the access token expires (after 2 hours), use the refresh token to get a new one:

const response = await fetch("https://clerk.coloop.ai/oauth/token", {
  method: "POST",
  headers: {
    "Content-Type": "application/x-www-form-urlencoded",
  },
  body: new URLSearchParams({
    grant_type: "refresh_token",
    client_id: "your_client_id",
    refresh_token: "your_refresh_token",
    // Include client_secret only for private applications
    ...(isPrivateApp && { client_secret: "your_client_secret" })
  })
});
const tokens = await response.json();

Security Considerations

  1. Token Storage
    • Store access tokens and refresh tokens securely
    • For public applications, use secure browser storage mechanisms
    • For private applications, use server-side secure storage
  2. PKCE Verifier
    • Generate a new code verifier for each authorization request
    • Store the verifier securely until the token exchange
  3. Error Handling
    • Handle token expiration and refresh scenarios gracefully
    • Implement retry logic with exponential backoff for failed requests