Authsome

OAuth2 Provider Plugin

Turn Authsome into an OAuth2 authorization server with Authorization Code + PKCE, Client Credentials, OIDC discovery, and client management.

The oauth2provider plugin turns Authsome into a full OAuth2 authorization server. It implements the Authorization Code grant with PKCE (RFC 7636), Client Credentials grant, token revocation (RFC 7009), OIDC UserInfo, and OpenID Connect Discovery.

Setup

import (
    "github.com/xraph/authsome"
    "github.com/xraph/authsome/plugins/oauth2provider"
)

eng, err := authsome.NewEngine(
    authsome.WithStore(store),
    authsome.WithPlugin(oauth2provider.New(oauth2provider.Config{
        Issuer:         "https://auth.example.com",
        AuthCodeTTL:    10 * time.Minute,
        AccessTokenTTL: 1 * time.Hour,
    })),
)

Configuration

OptionTypeDefaultDescription
Issuerstring"https://localhost"OAuth2 issuer URL used in OIDC discovery
AuthCodeTTLtime.Duration10mLifetime of authorization codes
AccessTokenTTLtime.Duration1hLifetime of issued access tokens

Implemented interfaces

InterfacePurpose
PluginBase plugin identity ("oauth2provider")
OnInitCaptures store, logger, session config resolver, and token format resolver
RouteProviderRegisters authorization, token, revoke, userinfo, discovery, and admin endpoints
MigrationProviderContributes oauth2_clients and oauth2_auth_codes tables

Authorization Code Grant with PKCE

The Authorization Code flow with PKCE is the recommended grant type for both public and confidential clients.

Step 1: Authorization request

GET /v1/auth/oauth/authorize

?response_type=code
&client_id=abc123
&redirect_uri=https://app.example.com/callback
&scope=openid profile email
&state=random-state
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256

The user must be authenticated. The endpoint validates the client, redirect URI, and PKCE challenge, then redirects back with an authorization code.

Step 2: Token exchange

POST /v1/auth/oauth/token

{
  "grant_type": "authorization_code",
  "code": "the-authorization-code",
  "redirect_uri": "https://app.example.com/callback",
  "client_id": "abc123",
  "client_secret": "secret",
  "code_verifier": "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
}

The plugin validates the code, checks PKCE, authenticates the client (for confidential clients), and issues tokens.

Response:

{
  "access_token": "eyJhbGciOiJS...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "d72b1ef8...",
  "scope": "openid profile email"
}

PKCE verification

The plugin supports both S256 and plain challenge methods. S256 is the recommended method:

  • The client generates a random code_verifier
  • Computes code_challenge = BASE64URL(SHA256(code_verifier))
  • Sends code_challenge in the authorization request
  • Sends code_verifier in the token request
  • The server verifies that BASE64URL(SHA256(code_verifier)) == code_challenge

Client Credentials Grant

Machine-to-machine authentication for confidential clients:

POST /v1/auth/oauth/token

{
  "grant_type": "client_credentials",
  "client_id": "abc123",
  "client_secret": "secret"
}

Response:

{
  "access_token": "a3f8c9d4...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "openid profile email"
}

Client credentials tokens have no associated user. They are issued with an empty user ID for service-to-service communication.

Token revocation

POST /v1/auth/oauth/revoke

{
  "token": "the-access-or-refresh-token",
  "token_type_hint": "access_token"
}

Follows RFC 7009. Always returns 200 OK regardless of whether the token was found.

OIDC UserInfo

GET /v1/auth/oauth/userinfo

Returns claims about the authenticated user (requires a valid access token):

{
  "sub": "ausr_01j9...",
  "email": "alice@example.com",
  "email_verified": true,
  "name": "Alice Liddell",
  "phone": "+14155551234"
}

OpenID Connect Discovery

GET /.well-known/openid-configuration

Returns the OIDC discovery document:

{
  "issuer": "https://auth.example.com",
  "authorization_endpoint": "https://auth.example.com/v1/auth/oauth/authorize",
  "token_endpoint": "https://auth.example.com/v1/auth/oauth/token",
  "userinfo_endpoint": "https://auth.example.com/v1/auth/oauth/userinfo",
  "revocation_endpoint": "https://auth.example.com/v1/auth/oauth/revoke",
  "response_types_supported": ["code"],
  "grant_types_supported": ["authorization_code", "client_credentials"],
  "scopes_supported": ["openid", "profile", "email", "phone"],
  "code_challenge_methods_supported": ["S256", "plain"]
}

Client management

Create a client

POST /v1/auth/admin/oauth/clients

{
  "app_id": "aapp_01j9...",
  "name": "My SPA",
  "redirect_uris": ["https://app.example.com/callback"],
  "scopes": ["openid", "profile", "email"],
  "grant_types": ["authorization_code"],
  "public": true
}

Response:

{
  "id": "aocl_01j9...",
  "client_id": "abc123def456...",
  "client_secret": "secret-shown-only-once",
  "name": "My SPA",
  "redirect_uris": ["https://app.example.com/callback"],
  "scopes": ["openid", "profile", "email"],
  "grant_types": ["authorization_code"],
  "public": true
}

The client_secret is only returned at creation time. It is bcrypt-hashed before storage. Public clients (SPAs, mobile apps) do not receive a secret.

List clients

GET /v1/auth/admin/oauth/clients?app_id=aapp_01j9...

Delete a client

DELETE /v1/auth/admin/oauth/clients/:clientId

API routes

MethodPathDescription
GET/v1/auth/oauth/authorizeOAuth2 authorization endpoint
POST/v1/auth/oauth/tokenToken endpoint (code exchange, client credentials)
POST/v1/auth/oauth/revokeToken revocation
GET/v1/auth/oauth/userinfoOIDC UserInfo
GET/.well-known/openid-configurationOIDC Discovery
POST/v1/auth/admin/oauth/clientsCreate OAuth2 client
GET/v1/auth/admin/oauth/clientsList OAuth2 clients
DELETE/v1/auth/admin/oauth/clients/:clientIdDelete OAuth2 client

JWT access tokens

When the engine has a JWT token format configured for the app, the plugin issues JWT access tokens instead of opaque tokens. The JWT includes sub, app_id, session_id, scopes, iat, and exp claims.

On this page