Skip to content
Docs · Your own APIs

Your own APIs

An app can ask for an access token for one of your APIs. It gets a signed JWT whose audience is that API and nothing else, and your API checks it with your environment’s published keys, without calling Usher. A token for one API is no use at another.

Name the API

In Usher Console, open the environment, then Applications → Your APIs → Add an API. Give it a name and an identifier: an https:// address, such as https://api.example.com, or a URN, such as urn:example:api:notes. The identifier is what the app asks for and what the token’s aud claim says (RFC 8707).

Then give the API to the apps that call it: APIs on each application. An app gets tokens only for the APIs it has been given, whatever it asks for. Remove an API and it gives no more tokens; the ones already out run out within minutes.

Ask for it

The app adds resource=<identifier> to its authorization request and its token requests. The mobile SDKs do this for you:

swift
let usher = UsherClient(configuration: .init(
    issuer: URL(string: "https://login.example.com")!,
    clientID: "…",
    redirectURI: URL(string: "com.example.notes:/oauth/callback")!,
    resources: ["urn:example:api:notes"]))

var request = URLRequest(url: URL(string: "https://api.example.com/notes")!)
try await usher.authorize(&request)   // Authorization: Bearer <a JWT for the Notes API>
kotlin
val usher = Usher.client(context, UsherConfiguration(
    issuer = "https://login.example.com",
    clientId = "…",
    redirectUri = "com.example.notes:/oauth/callback",
    resources = listOf("urn:example:api:notes")))

An app that calls two of your APIs can ask for both at once; the token’s audience then lists both.

Check it in your API

Check every request’s token before you trust anything in it:

  • The signature, against the keys at the jwks_uri in your environment’s discovery document. Take the algorithm from the key, never from the token’s header.
  • iss is exactly your environment’s issuer. Fix it in your configuration; never read it from the token.
  • aud includes your API’s identifier. (With the openid scope it also lists your environment’s userinfo address.)
  • typ in the header is at+jwt, so an ID token can’t be passed off as an access token.
  • exp has not passed. Access tokens last 10 minutes; the SDKs refresh them for you.

In Node.js, @usher/node does all of it:

TypeScript
import { createVerifier } from '@usher/node'

const usher = await createVerifier({
  issuer: 'https://login.example.com',
  audience: 'urn:example:api:notes',
})

const claims = await usher.accessToken(bearerToken) // throws if any check fails

In ASP.NET Core:

csharp
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(o =>
    {
        o.Authority = "https://login.example.com"; // discovery and keys come from here
        o.MapInboundClaims = false;
        o.TokenValidationParameters = new()
        {
            ValidIssuer = "https://login.example.com",
            ValidAudience = "urn:example:api:notes",
            ValidTypes = ["at+jwt"],
        };
    });

What the token says

Claim
subThe person. Together with iss, the key to keep them by.
client_id, azpThe app that asked.
scopeWhat the person agreed the app may do.
email, email_verifiedWith the email scope: the person’s address, and whether Usher has proved it’s theirs.
sidTheir sign-in session.
orgWith the organization scope: their active organisation and role.

People you already know

If your API already has accounts from before, you can find a person’s the first time they arrive with an Usher token: look them up by email, only when email_verified is true, and keep iss and sub on their account from then on. After that, find them by iss and sub, never by email: an address can change hands, and a subject never does.