Examples

Here are a few example authorizers to get you started. Most applications will use a variation on one of these methods.

Example: JWT Verification using JWKs

In this case, the legacy authentication method uses JWTs. The public key to verify the JWTs can be retrieved from a public JWKS endpoint. This code includes two secrets: PASSAGE_API_KEY and PUBLIC_JWKS. The Passage API Key is required for the context.getOrCreateUser() function to work.

import * as jose from "https://deno.land/x/[email protected]/index.ts";

export default async (event: PassageEvent, context: AuthorizerContext) => {
  const token = context.getAuthHeaderToken();
  if (!token) throw new Error("Missing Authorization header");
  
  const PUBLIC_JWKS = event.secrets.PUBLIC_JWKS;
  if (!PUBLIC_JWKS) throw new Error("Missing PUBLIC_JWKS secret");
  
  try {
    const jwks = JSON.parse(PUBLIC_JWKS);
    const JWKS = jose.createLocalJWKSet(jwks);
    const { payload } = await jose.jwtVerify(token, JWKS);
    const email = payload.email as string;

    if (email) {
      const passageUser = await context.getOrCreateUser(email);
      
      // Here you may want to link your User with a PassageUser
      // This can be done by either storing the PassageUser Id in your system
      // Or by storing your User ID as metadata on the Passage User
      
      context.allowAccess(passageUser.id);
    } else {
      context.denyAccess("User not found");
    }
  } catch (error) {
    context.denyAccess(error.message);
  }
};
Example: JWT Verification using Public Key

In this case, the legacy authentication method uses JWTs. The public key to verify the JWTs can be stored in a secret. This code includes two secrets: PASSAGE_API_KEY and PUBLIC_KEY. The Passage API Key is required for the context.getOrCreateUser() function to work.

import * as jose from "https://deno.land/x/[email protected]/index.ts";

export default async (event: PassageEvent, context: AuthorizerContext) => {
  const token = context.getAuthHeaderToken();
  if (!token) throw new Error("Missing Authorization header");
  
  const PUBLIC_KEY= event.secrets.PUBLIC_KEY;
  if (!PUBLIC_KEY) throw new Error("Missing PUBLIC_KEY secret");
  
  try {
    const { payload } = await jose.jwtVerify(token, PUBLIC_KEY);
    const email = payload.email as string;

    if (email) {
      const passageUser = await context.getOrCreateUser(email);
      
      // Here you may want to link your User with a PassageUser
      // This can be done by either storing the PassageUser Id in your system
      // Or by storing your User ID as metadata on the Passage User
      
      context.allowAccess(passageUser.id);
    } else {
      context.denyAccess("User not found");
    }
  } catch (error) {
    context.denyAccess(error.message);
  }
};
Example: JWT Verification using JWT Secret

In this case, the legacy authentication method uses JWTs. The JWT Secret can be stored in a secret. This code includes two secrets: PASSAGE_API_KEY and JWT_SECRET. The Passage API Key is required for the context.getOrCreateUser() function to work.

import * as jose from "https://deno.land/x/[email protected]/index.ts";

export default async (event: PassageEvent, context: AuthorizerContext) => {
  const token = context.getAuthHeaderToken();
  if (!token) throw new Error("Missing Authorization header");
  
  const JWT_SECRET = event.secrets.JWT_SECRET;
  if (!JWT_SECRET) throw new Error("Missing JWT_SECRET secret");
  
  try {
    const secret = new TextEncoder().encode(SUPABASE_JWT_SECRET);
    const { payload } = await jose.jwtVerify(token, secret);
    const email = payload.email as string;

    if (email) {
      const passageUser = await context.getOrCreateUser(email);
      
      // Here you may want to link your User with a PassageUser
      // This can be done by either storing the PassageUser Id in your system
      // Or by storing your User ID as metadata on the Passage User
      
      context.allowAccess(passageUser.id);
    } else {
      context.denyAccess("User not found");
    }
  } catch (error) {
    context.denyAccess(error.message);
  }
};
Example: Stateful Sessions using an API for Verification

In this case, the legacy authentication method uses stateful sessions that are a random string of bytes. Session tokens are stored in the legacy database and verified when requests are made. To authenticate a user, this authorizer makes an API call to an endpoint that returns the current user’s information based on the legacy session token provided. This code only requires one secret: PASSAGE_API_KEY. The Passage API Key is required for the context.getOrCreateUser() functions to work.

export default async (event: PassageEvent, context: AuthorizerContext) => {
  const token = context.getAuthHeaderToken();
  if (!token) throw new Error("Missing Authorization header");
  
  try {
    const resp = await fetch(
       "https://00d3a2884575.ngrok.io/currentuser", 
       {
          headers: {
            Cookie: "session=" + token
          } 
        }
    )
    if (resp.redirected) { // or your app may return a different status code
      context.denyAccess("invalid response")
      return
    }
    const data = await resp.json()
    const passageUser = await context.getOrCreateUser(data.email)
    
    // Here you may want to link your User with a PassageUser
    // This can be done by either storing the PassageUser Id in your system
    // Or by storing your User ID as metadata on the Passage User    
    
    context.allowAccess(passageUser.id)
  } catch (err) {
    context.denyAccess(err.message)
  }
}

Last updated

Change request #337: react native