🛡️Gatekeeper/ SDKs

Gatekeeper has two distinct credential planes: short-lived bearer access tokens for user sessions, and long-lived API keys for machine-to-machine traffic. This page covers both, plus the MFA challenge flow and OAuth.

Bearer access tokens#

login and signup return a token bundle: an accessToken, a refreshToken, an expiresIn, and the user. Apply the access token to the core with setToken / set_token, and every authenticated call thereafter sends Authorization: Bearer <token>.

import { GatekeeperCore, AuthService } from '@orkait/sdk';
 
const core = new GatekeeperCore({ baseUrl: 'http://localhost:8787' });
const auth = new AuthService(core);
 
const { accessToken, refreshToken } = await auth.login({
  email: 'founder@example.com',
  password: 'hunter2pass',
});
 
core.setToken(accessToken);
const me = await auth.me(); // now authenticated

Refresh and rotation#

Access tokens are short-lived. When one expires, exchange the refreshToken for a fresh bundle with refresh. The server rotates the refresh token, so always replace your stored refresh token with the new one from the response. Call logout to invalidate a refresh token.

  1. Keep the refreshToken from login somewhere durable.
  2. When the access token expires, call refresh(refreshToken) for a new bundle.
  3. Re-apply the new access token and persist the new refresh token (rotation).
  4. On sign-out, call logout(refreshToken).
// Rotate: exchange the old refresh token for a fresh bundle.
const next = await auth.refresh(refreshToken);
core.setToken(next.accessToken);
const newRefreshToken = next.refreshToken; // persist this, the old one is rotated out
 
// Sign out: invalidate the refresh token.
await auth.logout(newRefreshToken);
core.setToken(undefined);

API keys#

API keys are the machine-to-machine credential, scoped to a tenant. Create a key with KeysService, then either validate the raw key, or exchange it for a short-lived RS256-signed JWT for downstream services that prefer to verify a signature.

Validate a key#

validate returns the key's metadata (tenant, scopes, status, quota) if the key is good, or throws / raises GatekeeperError if it is not.

import { KeysService } from '@orkait/sdk';
 
const keys = new KeysService(core);
const validated = await keys.validate('gk_live_...'); // raw key
console.log(validated.tenantId, validated.scopes);

Exchange a key for an RS256 token#

token swaps a raw API key for a signed JWT (token plus tokenType). Downstream services can verify it against the published JWKS instead of calling back to validate on every request.

const exchanged = await keys.token('gk_live_...');
console.log(exchanged.token, exchanged.tokenType);

MFA challenge flow#

When a user has MFA enabled, login returns a challenge (mfaRequired: true plus a challengeToken) rather than tokens. Detect it with isMfaChallenge / is_mfa_challenge, prompt the user for their TOTP code, then complete the challenge with MfaService.verifyChallenge / verify_challenge, which returns the real token bundle.

  1. Call login. The result is either tokens or a challenge.
  2. Branch with isMfaChallenge / is_mfa_challenge.
  3. If it is a challenge, collect the 6-digit TOTP code from the user.
  4. Call verifyChallenge(challengeToken, code) to get the token bundle.
  5. Apply the access token to the core.
import { AuthService, MfaService, isMfaChallenge } from '@orkait/sdk';
 
const auth = new AuthService(core);
const result = await auth.login({ email: 'founder@example.com', password: 'hunter2pass' });
 
if (isMfaChallenge(result)) {
  const mfa = new MfaService(core);
  const code = await promptUserForTotp(); // your UI
  const tokens = await mfa.verifyChallenge(result.challengeToken, code);
  core.setToken(tokens.accessToken);
} else {
  core.setToken(result.accessToken);
}

OAuth#

For social or SSO login, OAuthService produces an authorization URL and consumes the provider callback. Send the user to the returned url, keep the state, and when the provider redirects back, hand the callback parameters to callback to receive the token bundle.

  1. Call authorize(provider) to get a redirect url and a state.
  2. Redirect the user to url; persist state to compare on return.
  3. On the provider redirect back, call callback with state and the code (or the error the provider returned).
  4. Apply the returned access token to the core.
import { OAuthService } from '@orkait/sdk';
 
const oauth = new OAuthService(core);
 
// 1. Begin the flow.
const { url, state } = await oauth.authorize('google');
// ...redirect the user to `url`, stash `state`...
 
// 3. Complete it when the provider redirects back with ?code=...&state=...
const tokens = await oauth.callback('google', { state, code: 'returned-code' });
core.setToken(tokens.accessToken);

Credential cheat sheet#

CredentialLifetimeHow you get itHow you apply it
Access tokenShortlogin / signup / refresh / verifyChallenge / OAuth callbacksetToken / set_token
Refresh tokenLong, rotatedSame token bundlePassed to refresh / logout
API keyLongkeys.create (plaintext shown once)validate or token exchange
RS256 JWTShortkeys.tokenVerified downstream against JWKS