Skip to content

Vanilla JS

This guide shows how to implement the MC-ID authentication flow using plain JavaScript and HTTP requests. This is useful for understanding the underlying OAuth 2.1/OIDC flow or for simple applications without a framework.

  • Client ID: Your application’s Client ID.
  • Client Secret: Your application’s Client Secret.
  • Redirect URI: The URL where MC-ID will redirect users after login.

First, you’ll need these helper functions to generate the PKCE code verifier and challenge:

  1. Add PKCE Helper Functions

    Add these utility functions to your application. They generate the cryptographic values required for PKCE; or provide your own implementations.

    // Generate a random code verifier (43-128 characters)
    function generateCodeVerifier() {
    const array = new Uint8Array(32);
    crypto.getRandomValues(array);
    return base64UrlEncode(array);
    }
    // Generate the code challenge from the verifier (S256 method)
    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));
    }
    // Base64url encode (RFC 4648)
    function base64UrlEncode(buffer) {
    return btoa(String.fromCharCode(...buffer))
    .replace(/\+/g, "-")
    .replace(/\//g, "_")
    .replace(/=+$/, "");
    }
    // Generate a random state for CSRF protection
    function generateState() {
    const array = new Uint8Array(16);
    crypto.getRandomValues(array);
    return base64UrlEncode(array);
    }
  2. Start the Authorization Flow

    Generate the PKCE values, store them, and redirect the user to MC-ID.

    const clientId = "YOUR_CLIENT_ID";
    const redirectUri = "http://localhost:3000/callback";
    const scope = "openid profile email connections";
    async function startAuth() {
    // Generate PKCE values
    const codeVerifier = generateCodeVerifier();
    const codeChallenge = await generateCodeChallenge(codeVerifier);
    const state = generateState();
    // Store verifier and state for the callback
    sessionStorage.setItem("code_verifier", codeVerifier);
    sessionStorage.setItem("oauth_state", state);
    // Build the authorization URL
    const authUrl = new URL("https://mc-id.com/api/auth/oauth2/authorize");
    authUrl.searchParams.append("client_id", clientId);
    authUrl.searchParams.append("redirect_uri", redirectUri);
    authUrl.searchParams.append("response_type", "code");
    authUrl.searchParams.append("scope", scope);
    authUrl.searchParams.append("state", state);
    authUrl.searchParams.append("code_challenge", codeChallenge);
    authUrl.searchParams.append("code_challenge_method", "S256");
    // Redirect the user
    window.location.href = authUrl.toString();
    }
  3. Handle the Callback

    On your callback page, verify the state parameter and extract the authorization code.

    // On your callback page
    const urlParams = new URLSearchParams(window.location.search);
    const code = urlParams.get("code");
    const returnedState = urlParams.get("state");
    // Verify state to prevent CSRF attacks
    const savedState = sessionStorage.getItem("oauth_state");
    if (returnedState !== savedState) {
    throw new Error("State mismatch - possible CSRF attack");
    }
    // Get the code verifier we saved earlier
    const codeVerifier = sessionStorage.getItem("code_verifier");
    if (code && codeVerifier) {
    exchangeCodeForToken(code, codeVerifier);
    }
    // Clean up
    sessionStorage.removeItem("code_verifier");
    sessionStorage.removeItem("oauth_state");
  4. Exchange Code for Tokens

    Send the code and the original code verifier to the token endpoint.

    async function exchangeCodeForToken(code, codeVerifier) {
    const tokenEndpoint = "https://mc-id.com/api/auth/oauth2/token";
    const clientId = "YOUR_CLIENT_ID";
    const clientSecret = "YOUR_CLIENT_SECRET";
    const redirectUri = "http://localhost:3000/callback";
    const response = await fetch(tokenEndpoint, {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams({
    grant_type: "authorization_code",
    code: code,
    client_id: clientId,
    client_secret: clientSecret,
    redirect_uri: redirectUri,
    code_verifier: codeVerifier // Required for PKCE
    })
    });
    const data = await response.json();
    console.log("Tokens:", data);
    // data.access_token - Use this to call the userinfo endpoint
    // data.id_token - Contains user claims (JWT)
    // data.refresh_token - Only if you requested offline_access scope
    await getUserInfo(data.access_token);
    }
  5. Get User Info

    Use the access_token to retrieve the user’s profile information from the UserInfo endpoint.

    async function getUserInfo(accessToken) {
    const response = await fetch("https://mc-id.com/api/auth/oauth2/userinfo", {
    headers: {
    Authorization: `Bearer ${accessToken}`
    }
    });
    const userProfile = await response.json();
    console.log("User Profile:", userProfile);
    return userProfile;
    }

    Example response (claims depend on requested scopes):

    {
    "sub": "AaltJ3XUoyDQiqDVk865CILljZBXrjZz",
    "name": "Notch",
    "email": "user@example.com",
    "email_verified": true,
    "accounts": [
    {
    "uuid": "069a79f444e94726a5befca90e38aaf5",
    "primary": true,
    "username": "Notch"
    }
    ],
    "connections": [
    {
    "providerId": "discord",
    "accountId": "123456789012345678"
    }
    ]
    }

See MC-ID Scopes