OAuth 2.0
OAuth 2.0 is an industry-standard authorization framework. Roled uses it as the foundation for authentication, not because it’s complex, but because it’s a proven and secure way to issue and manage access tokens.
What Is OAuth 2.0?
OAuth 2.0 is a protocol that allows applications to obtain limited access to user accounts or services. At its core, it defines a set of flows (called “grant types”) that describe how a client application can get an access token.
OAuth 2.0 involves four key components:
- Authorization Server: The central authority that authenticates users and issues access tokens.
- Client: The application that wants to access the user’s data. The client is identified by a
client_idandclient_secret. - Resource Owner: The user who owns the data that the client wants to access.
- Resource Server: The server that hosts the user’s data. The resource server validates the access token and returns the requested data.
In the context of Roled, all components are within the Roled infrastructure, excluding the client, which is your own application or service accessing Roled’s protected resources. Roled acts as an authorization server and resource server at the same time, providing the user role and permissions information defined in your projects.
Here is how it works:
sequenceDiagram
autonumber
participant Y as Client (Business App)
box rgb(255, 245, 173) Roled
participant A as Authorization Server (Roled Auth)
participant U as Resource Owner (User)
participant R as Resource Server (Roled API)
end
Y->>U: Request login via Roled
U->>A: Authenticate
A-->>Y: Redirect with auth code
Y->>A: Exchange auth code for tokens
A-->>Y: access_token, refresh_token
Y->>R: Call protected endpoint
R->>R: Validate access_token
R-->>Y: Return resource data
How Roled Uses OAuth 2.0
Roled implements two OAuth 2.0 grant types:
| Grant Type | Used By | Purpose |
|---|---|---|
| Authorization Code | Human users | Browser-based login flow |
| Client Credentials | Machine clients | Server-to-server authentication |
Roled deliberately does not implement the Implicit flow or Resource Owner Password Credentials flow, as these are considered insecure by modern OAuth 2.0 best practices in RFC 9700 document section 2.1.2 ↗ and section 2.4 ↗.
Authorization Code Flow (with PKCE)
This flow is used when a user (a human) logs in through a browser. Roled implements it with PKCE (Proof Key for Code Exchange) for added security.
PKCE prevents authorization code interception attacks by requiring the client to prove it initiated the flow.
sequenceDiagram
autonumber
participant U as User
participant Y as Your App
participant R as Roled
U->>Y: Request login
Y->>Y: Generate code_verifier (random string)
Y->>Y: Generate code_challenge = SHA256(code_verifier)
Y->>R: GET /authorize?<br/>client_id=...&<br/>redirect_uri=...&<br/>response_type=code&<br/>code_challenge=...&<br/>code_challenge_method=S256
R-->>U: Display login page
U->>R: Submit email + password
R-->>Y: Redirect to redirect_uri?code=AUTH_CODE&...
Y->>R: POST /api/v1/tokens<br/>grant_type=authorization_code&<br/>code=AUTH_CODE&<br/>code_verifier=...
R->>R: Verify code + code_verifier
R-->>Y: access_token + refresh_token
Y-->>U: Logged in
Explanation:
- User opens a login page in your app, your app generates a
code_verifier(random string) andcode_challenge(SHA256 hash ofcode_verifier) - Your app redirects the user to Roled’s authorization endpoint with:
client_id: Your project’s client IDredirect_uri: Where to send the user after login (must be registered in the project)response_type: Alwayscodecode_challenge: The generated code challenge, base64url-encodedcode_challenge_method: AlwaysS256(plain is not supported)state: Optional random string to prevent CSRF attacks (recommended)
- Roled displays the login page
- User submits their email and password
- Roled redirects the user back to your
redirect_uriwith an authorization code - Your app exchanges the authorization code for an access token by sending a POST request to Roled’s token endpoint with:
grant_type:authorization_codeclient_id: Your project’s client IDauthorization_code: The code received in the previous stepredirect_uri: The same redirect URI used in step 4code_verifier: The original code verifier generated in step 2
- Roled verifies the code and code verifier, then returns:
access_token: The access token for accessing protected resourcesrefresh_token: The refresh token for getting new access tokens
- The user is now logged in and can access protected resources
Exchange the code for an access token:
curl --request POST \
--url https://auth.roled.id/api/v1/tokens \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'authorization_code=6qxJpdxpyuAoSpfzkEbJUpJvBoncYM3Cnxfmj4WznZty7...' \
--data-urlencode 'client_id=2JRYkzSeBV89itqcU9mraF' \
--data-urlencode 'code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk...' \
--data-urlencode 'redirect_uri=https://example.com/auth/callback' \
--data-urlencode 'state=e5384378-26b2-45ae-b6a8-3a569a4bff7d'Client Credentials Flow
This flow is used when a machine (a backend service or script) needs to authenticate. There is no user involved.
sequenceDiagram
autonumber
participant Service as Backend Service
participant Roled
Service->>Roled: POST /api/v1/tokens<br/>Authorization: Basic base64(client_id:client_secret)<br/>grant_type=client_credentials
Roled->>Roled: Validate client credentials
Roled->>Roled: Issue access token for client
Roled-->>Service: access_token (no refresh token)
curl --request POST \
--url https://auth.roled.id/api/v1/tokens \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Authorization: Basic MkpVjd5QzdnV0V1enU4QlRFWG9ZNTpzZWNfQmFja...' \
--data-urlencode 'grant_type=client_credentials'Client tokens do not include a refresh token. When a client token expires, the client simply re-authenticates.
Refresh Token Flow
For user sessions (Authorization Code Flow), Roled also supports the refresh token grant. This allows your application to get a new access token when the current one expires, without forcing the user to log in again.
curl --request POST \
--url https://auth.roled.id/api/v1/tokens \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=refresh_token' \
--data-urlencode 'client_id=2JRYkzSeBV89itqcU9mraF' \
--data-urlencode 'refresh_token=M8MmHrhqzcBgUwCQ5enp4wTuRVBgi2txChS44Q3FYJGRgzX...'OAuth Scopes
In traditional OAuth 2.0 setups, applications request specific scopes during authentication to specify required permissions (e.g., read:users, write:posts).
Roled takes a streamlined approach: user and client permissions are deliberately managed and assigned by project administrators rather than requested dynamically by applications.
Instead of requiring applications to request individual scopes at login, Roled automatically evaluates access based on the roles and permissions assigned to the user or client. Project administrators define exactly who can perform which actions, while applications simply authenticate and let Roled enforce access control.
This has several advantages:
- Simpler for developers — no need to understand or declare scopes.
- Centrally managed — administrators change permissions without touching application code.
- Consistent model — the same authorization logic applies to both users and machine clients.
- Instant effect — permission changes take effect immediately without requiring token reissuance.
The Authorize Endpoint
The OAuth 2.0 authorization endpoint for Roled is:
GET https://auth.roled.id/authorizeRequired query parameters:
| Parameter | Value |
|---|---|
client_id | Your client’s ID |
redirect_uri | A registered redirect URI |
response_type | code |
code_challenge | SHA256 of code_verifier, base64url-encoded |
code_challenge_method | S256 |
state | (optional) CSRF protection value |
Key Points
- Roled uses Authorization Code + PKCE for users and Client Credentials for machines.
- PKCE (
code_challenge_method=S256) is required — plain is not supported. - Roled does not use OAuth scopes — permissions are managed by project administrators.
- The Implicit flow and ROPC are intentionally not supported.
- Every token is issued against a specific project (the
audclaim).