Optare v1.0 is now available. Get started →
Quickstarts
Vanilla JavaScript

Add Login with Vanilla JavaScript

This quickstart shows how to integrate Optare with pure JavaScript - no React, Vue, or other frameworks.

Prerequisites

  • A web server (or use npx serve)
  • An Optare account

1. Get Your Client ID

  1. Log in to Optare Console (opens in a new tab)
  2. Create an OAuth client:
    • Type: Single Page Application
    • Redirect URI: https://yourapp.com/callback.html
  3. Copy the Client ID

2. Create Login Page

Create index.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>My App</title>
</head>
<body>
  <div id="app">
    <button id="login-btn">Log In</button>
    <div id="user-info" style="display: none;">
      <p>Welcome, <span id="user-name"></span>!</p>
      <button id="logout-btn">Log Out</button>
    </div>
  </div>
 
  <script>
    const OPTARE_DOMAIN = 'https://id.optare.one';
    const CLIENT_ID = 'your_client_id';
    const REDIRECT_URI = 'https://yourapp.com/callback.html';
 
    // Generate PKCE code verifier
    function generateCodeVerifier() {
      const array = new Uint8Array(32);
      crypto.getRandomValues(array);
      return btoa(String.fromCharCode(...array))
        .replace(/\+/g, '-')
        .replace(/\//g, '_')
        .replace(/=/g, '');
    }
 
    // Generate PKCE 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 btoa(String.fromCharCode(...new Uint8Array(digest)))
        .replace(/\+/g, '-')
        .replace(/\//g, '_')
        .replace(/=/g, '');
    }
 
    // Login
    document.getElementById('login-btn').onclick = async () => {
      const verifier = generateCodeVerifier();
      const challenge = await generateCodeChallenge(verifier);
      
      // Store verifier for callback
      sessionStorage.setItem('code_verifier', verifier);
 
      const params = new URLSearchParams({
        client_id: CLIENT_ID,
        redirect_uri: REDIRECT_URI,
        response_type: 'code',
        scope: 'openid profile email',
        code_challenge: challenge,
        code_challenge_method: 'S256',
      });
 
      window.location.href = `${OPTARE_DOMAIN}/oauth/authorize?${params}`;
    };
 
    // Check if logged in
    const token = localStorage.getItem('access_token');
    if (token) {
      document.getElementById('login-btn').style.display = 'none';
      document.getElementById('user-info').style.display = 'block';
      
      // Decode token to get email
      const payload = JSON.parse(atob(token.split('.')[1]));
      document.getElementById('user-name').textContent = payload.email;
    }
 
    // Logout
    document.getElementById('logout-btn').onclick = () => {
      localStorage.removeItem('access_token');
      window.location.reload();
    };
  </script>
</body>
</html>

3. Create Callback Page

Create callback.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Logging in...</title>
</head>
<body>
  <p>Logging you in...</p>
 
  <script>
    const OPTARE_DOMAIN = 'https://id.optare.one';
    const CLIENT_ID = 'your_client_id';
    const REDIRECT_URI = 'https://yourapp.com/callback.html';
 
    async function handleCallback() {
      const params = new URLSearchParams(window.location.search);
      const code = params.get('code');
      const error = params.get('error');
 
      if (error) {
        alert('Login failed: ' + error);
        window.location.href = '/';
        return;
      }
 
      if (!code) {
        alert('No authorization code received');
        window.location.href = '/';
        return;
      }
 
      const verifier = sessionStorage.getItem('code_verifier');
      sessionStorage.removeItem('code_verifier');
 
      // Exchange code for tokens
      const response = await fetch(`${OPTARE_DOMAIN}/oauth/token`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          grant_type: 'authorization_code',
          code,
          client_id: CLIENT_ID,
          redirect_uri: REDIRECT_URI,
          code_verifier: verifier,
        }),
      });
 
      const tokens = await response.json();
 
      if (tokens.error) {
        alert('Token exchange failed: ' + tokens.error);
        window.location.href = '/';
        return;
      }
 
      // Store tokens
      localStorage.setItem('access_token', tokens.access_token);
      if (tokens.refresh_token) {
        localStorage.setItem('refresh_token', tokens.refresh_token);
      }
 
      // Redirect to app
      window.location.href = '/';
    }
 
    handleCallback();
  </script>
</body>
</html>

4. Run the App

npx serve .

Open https://yourapp.com (opens in a new tab)!


Security Notes

  • This example uses PKCE (Proof Key for Code Exchange) for security
  • Tokens are stored in localStorage - consider using memory storage for higher security
  • For production, use the SDK instead of raw API calls

Next Steps