Authentication
This page shows you how to get an access token for Goiabada’s APIs and call them with it.
Goiabada has two APIs, both served by the auth server:
- The Admin API, under
/api/v1/admin/, manages users, groups, clients, resources, permissions and settings. It’s the API the admin console itself calls. A service calls it with a token of its own. - The Account API, under
/api/v1/account/, lets a signed-in user manage their own account: profile, email, password, two-factor authentication, sessions and consents. Your app calls it with the user’s token.
Every operation is in the Admin API and Account API reference, with a curl example.
Call the Admin API
Section titled “Call the Admin API”A service that manages Goiabada on its own, with no user present, gets its token through the client credentials flow: it signs in as a client, with the client’s own permissions.
-
In the admin console, open Admin, Clients and click Create new. Enter a Client identifier, such as
provisioning-service, turn on Client credentials flow, turn off Authorization code flow with PKCE, and click Create. -
Click Manage next to the new client, open Authentication and copy the Client secret.
-
Open Permissions, choose the
authserverresource and the permission your service needs, such asmanage-users, click Grant permission and then Save. Scopes explains what each permission allows. -
Get an access token, asking for the scope that matches the permission:
Terminal window curl -X POST https://auth.example.com/auth/token \-d grant_type=client_credentials \-d client_id=provisioning-service \-d client_secret=YOUR_CLIENT_SECRET \-d scope=authserver:manage-usersThe answer carries the token in
access_token, and its lifetime in seconds inexpires_in. -
Call the API with the token in the
Authorizationheader:Terminal window curl "https://auth.example.com/api/v1/admin/users/search?query=ana" \-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Ask for several scopes at once by separating them with spaces, as in scope=authserver:manage-users authserver:manage-clients. When the token expires, request a new one the same way.
Each of manage, admin-read, manage-users, manage-clients and manage-settings makes the client an administrator, so only an administrator holding authserver:manage can grant one. The administrator the setup created holds it.
Call the Account API
Section titled “Call the Account API”The Account API acts on the account of the user the token was issued for, so it needs a token your app got for a signed-in user, through the authorization code flow. A token from the client credentials flow is refused, as below.
-
Send the user to the authorization endpoint, asking for
authserver:manage-accountbesideopenid:GET https://auth.example.com/auth/authorize?client_id=my-app&redirect_uri=https://my-app.example.com/callback&response_type=code&scope=openid%20authserver:manage-account&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256&state=abc123The first time, the user sees the consent screen, which names your app and says what
authserver:manage-accountlets it do, and approves it. Their answer is kept, so each user is asked once. -
Exchange the code the user comes back with for tokens:
Terminal window curl -X POST https://auth.example.com/auth/token \-d grant_type=authorization_code \-d client_id=my-app \-d client_secret=YOUR_CLIENT_SECRET \-d code=THE_CODE \-d redirect_uri=https://my-app.example.com/callback \-d code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk -
Call the API with the
access_tokenfrom the answer:Terminal window curl https://auth.example.com/api/v1/account/profile \-H "Authorization: Bearer USER_ACCESS_TOKEN"
Every user holds the manage-account permission from the moment they’re created, and it isn’t an administrative permission, so your client needs no permission of its own, and no allowance, to ask for it. What it needs is the user’s approval. The consent screen is shown the first time each user signs in to your app asking for authserver:manage-account, whatever your client’s Consent required says, and again if they untick it or withdraw it under Account, Manage consents. Add sign-in to a web app covers the sign-in itself, PKCE included.
Until a user has approved it, your app can’t get the scope without them:
- a
prompt=nonerequest asking for it is answeredconsent_required; - a refresh renewing it is refused with
invalid_grant, and the refresh token isn’t spent, so a refresh whosescopeleavesauthserver:manage-accountout still works.
A session or a live refresh token from an earlier authorization-code sign-in can encounter these checks until the user approves the scope. Send the user through an ordinary sign-in: they approve once, and the refreshes and silent requests after it work. Refresh tokens from the password grant don’t need this consent.
Change a user’s account from your app
Section titled “Change a user’s account from your app”There are three ways, and which one fits depends on who’s making the change:
- The Account API, with the user’s token. For an app that changes the signed-in user’s own account on their behalf. It asks for
authserver:manage-accountas above, and each user approves that once on the consent screen. The token reaches that user’s account and nothing else. - The Admin API, with your client’s own token. For a service that changes users’ accounts with no user present, such as provisioning from an HR system. It gets its token through the client credentials flow, as in Call the Admin API, holding
authserver:manage-users, which only an administrator withauthserver:managecan grant it. That makes the client an administrator, and it still can’t change an administrator. - A link to the admin console’s Account pages. For a change the user makes themselves. Link to
/account/profileon the admin console, such ashttps://admin.example.com/account/profile. The user signs in there if they need to and edits their profile, email, phone, address, picture, password, two-factor authentication, sessions and consents. Your app needs no scope at all.
The bearer token
Section titled “The bearer token”Every operation under /api/v1/ takes an access token in the Authorization header, as Bearer followed by the token. The reference also lists one public operation outside /api/v1/, the client logo, GET /client/logo/{clientIdentifier}, which takes none.
The token must be an access token the auth server issued for the authserver resource, which is what asking for an authserver: scope gets you. An ID token or a refresh token is refused.
Which tokens each API accepts
Section titled “Which tokens each API accepts”The Admin API accepts a client’s token from the client credentials flow, and a user’s token from the authorization code flow when the client may request the administrative scopes on a user’s behalf.
The Account API accepts only a token issued for a user. A token from the client credentials flow is answered 403: with INSUFFICIENT_SCOPE when it doesn’t carry authserver:manage-account, which is the usual case, and with USER_CONTEXT_REQUIRED when its client was granted that permission.
When a token stops working
Section titled “When a token stops working”A user’s token is checked against the user and their session on every call, so it stops working before it expires once the user is disabled or the session it was issued through ends. The call is then answered 401 with INVALID_TOKEN.
The other refusals:
| Answer | Error code | Why |
|---|---|---|
401 |
ACCESS_TOKEN_REQUIRED |
No token was sent. |
401 |
INVALID_TOKEN |
The token is malformed, expired, not an access token for authserver, or its user or session is gone. |
403 |
INSUFFICIENT_SCOPE |
The token carries none of the scopes the operation accepts. |
The errors page has the format of every error answer.
The OpenAPI document
Section titled “The OpenAPI document”The auth server serves the whole API as an OpenAPI 3.0 document at /openapi.yaml, with no authentication:
curl https://auth.example.com/openapi.yamlThis reference is generated from it, so the two always say the same thing. Import it into Postman or Swagger UI, or generate a client from it.