Chapter 7

Examples

The same four things in four places: sign in, keep the session, verify a token on your server, and deal with the code request.

Before you copy any of this

  • Every call is a POST to the method's own path, with a JSON body. There is no REST-style path building.
  • Users sign in with Google or Apple. There is no password to collect, so none of these examples has a password field.
  • The API key belongs in the server example and nowhere else. The header it uses is not allowed through CORS, so a browser cannot send one.
  • A browser keeps its session in a cookie; a native app keeps the tokens itself, in the Keychain or in encrypted preferences.
verify-token.ts
// Verifying a token your own front end sent you.
// This is the only call an API key is really for.

const API = "https://api.albos.org";
const API_KEY = process.env.CLOUD_AUTH_API_KEY!; // ca_...

export type VerifiedUser = {
  id: string;
  email: string;
  displayName: string;
};

export async function verifyAccessToken(
  accessToken: string,
): Promise<VerifiedUser | null> {
  const res = await fetch(`${API}/auth.v1.AuthService/VerifyToken`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-API-Key": API_KEY,
    },
    body: JSON.stringify({ accessToken }),
  });

  // 429 means the key's own allowance is spent. Back off; do not loop.
  // 403 means the key may not do this at all. Retrying will never help.
  if (!res.ok) throw new Error(`verify failed: ${res.status}`);

  const data = await res.json();

  // active is the answer. Do not read the token yourself.
  // Signing out and disabling an account take effect at once; a token still
  // inside its 15 minutes does not know that.
  if (!data.active) return null;

  return {
    id: data.user.id,
    email: data.user.email,
    displayName: data.user.displayName,
  };
}
These are shapes, not a library. There is no published SDK yet: the service speaks Connect RPC, so a generated client for your language is an option too — point buf at the same protos and the calls above become typed methods.