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:
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>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_uriin your environment’s discovery document. Take the algorithm from the key, never from the token’s header. issis exactly your environment’s issuer. Fix it in your configuration; never read it from the token.audincludes your API’s identifier. (With theopenidscope it also lists your environment’s userinfo address.)typin the header isat+jwt, so an ID token can’t be passed off as an access token.exphas not passed. Access tokens last 10 minutes; the SDKs refresh them for you.
In Node.js, @usher/node does all of it:
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 failsIn ASP.NET Core:
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 | |
|---|---|
sub | The person. Together with iss, the key to keep them by. |
client_id, azp | The app that asked. |
scope | What the person agreed the app may do. |
email, email_verified | With the email scope: the person’s address, and whether Usher has proved it’s theirs. |
sid | Their sign-in session. |
org | With 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.