OAuth2 Authentication

OAuth2 Authentication

Snapbooks API uses OAuth2 for secure authentication and authorization. This provides a robust framework for secure API access while allowing fine-grained control over permissions.

Overview

OAuth2 is an industry-standard protocol that allows secure API authorization in a simple and standard way. Snapbooks supports three OAuth2 grant types:

  1. Authorization Code Grant - For applications that can securely store client secrets (web applications)
  2. Authorization Code with PKCE - For public clients that cannot securely store secrets (mobile/single-page apps)
  3. Client Credentials Grant - For server-to-server API access

OAuth2 Endpoints

Endpoint Description
POST /snapbooks/api/v2/oauth/clients Register a new OAuth client application
GET /snapbooks/api/v2/oauth/clients List registered OAuth clients
DELETE /snapbooks/api/v2/oauth/clients/{client_id} Delete an OAuth client
GET /snapbooks/public/v2/oauth/authorize Authorization endpoint to obtain user consent
POST /snapbooks/public/v2/oauth/token Token endpoint to obtain access and refresh tokens
POST /snapbooks/public/v2/oauth/revoke Revocation endpoint to invalidate tokens

Client Registration

Before using OAuth2, you must register a client application:

Register a new client

POST /snapbooks/api/v2/oauth/clients
Authorization: Bearer {your-admin-token}
Content-Type: application/json

{
  "client_name": "My Accounting App",
  "redirect_uris": "https://myapp.example.com/callback",
  "grant_types": "authorization_code refresh_token",
  "scope": "read:accounts write:transactions"
}

Response:

{
  "client_id": "AbCdEfGhIjKlMnOpQrStUv",
  "client_secret": "AbCdEfGhIjKlMnOpQrStUvWxYz0123456789AbCdEfGhIjKlMnOpQrStUvWxYz",
  "client_name": "My Accounting App",
  "redirect_uris": "https://myapp.example.com/callback",
  "grant_types": "authorization_code refresh_token",
  "scope": "read:accounts write:transactions"
}

Important: Store the client_id and client_secret securely - the secret will not be retrievable later.

List registered clients

GET /snapbooks/api/v2/oauth/clients
Authorization: Bearer {your-admin-token}

Response:

{
  "clients": [
    {
      "client_id": "AbCdEfGhIjKlMnOpQrStUv",
      "client_name": "My Accounting App",
      "redirect_uris": "https://myapp.example.com/callback",
      "grant_types": "authorization_code refresh_token",
      "scope": "read:accounts write:transactions",
      "created_at": "2025-04-10T14:27:35.123456"
    }
  ]
}

Delete a client

DELETE /snapbooks/api/v2/oauth/clients/{client_id}
Authorization: Bearer {your-admin-token}

Response:

{
  "status": "success",
  "message": "Client deleted"
}

Authorization Code Flow

This flow is suitable for web applications that can securely store client secrets.

1. Request Authorization Code

GET /snapbooks/public/v2/oauth/authorize?
  response_type=code&
  client_id={client_id}&
  redirect_uri=https%3A%2F%2Fmyapp.example.com%2Fcallback&
  scope=read%3Aaccounts%20write%3Atransactions&
  state={random_state_value}

Parameters:

  • response_type: Must be code
  • client_id: Your application’s client ID
  • redirect_uri: URL-encoded callback URL (must match a pre-registered value)
  • scope: Space-separated list of requested permissions
  • state: Random string to prevent CSRF attacks

The user will be prompted to log in (if not already) and authorize your application’s access.

2. Exchange Code for Tokens

After the user authorizes access, they’re redirected to your application:

https://myapp.example.com/callback?code={authorization_code}&state={random_state_value}

Verify that the state matches the one you generated, then exchange the code for tokens:

POST /snapbooks/public/v2/oauth/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic {base64(client_id:client_secret)}

grant_type=authorization_code&
code={authorization_code}&
redirect_uri=https%3A%2F%2Fmyapp.example.com%2Fcallback

Response:

{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "def50200fe37bb71b1baba9b5f2d5...",
  "scope": "read:accounts write:transactions"
}

Authorization Code with PKCE

For public clients (like mobile/SPA apps) that can’t securely store secrets, use PKCE (Proof Key for Code Exchange):

1. Generate a Code Verifier and Challenge

// Generate a random code verifier
const codeVerifier = generateRandomString(64);

// Create the code challenge using S256 method
const codeChallenge = base64UrlEncode(sha256(codeVerifier));

2. Request Authorization Code with PKCE

GET /snapbooks/public/v2/oauth/authorize?
  response_type=code&
  client_id={client_id}&
  redirect_uri=https%3A%2F%2Fmyapp.example.com%2Fcallback&
  scope=read%3Aaccounts%20write%3Atransactions&
  state={random_state_value}&
  code_challenge={code_challenge}&
  code_challenge_method=S256

3. Exchange Code for Tokens with PKCE

POST /snapbooks/public/v2/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&
client_id={client_id}&
code={authorization_code}&
redirect_uri=https%3A%2F%2Fmyapp.example.com%2Fcallback&
code_verifier={code_verifier}

Client Credentials Flow

For server-to-server authentication without user involvement:

POST /snapbooks/public/v2/oauth/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic {base64(client_id:client_secret)}

grant_type=client_credentials&
scope=read%3Aaccounts%20write%3Atransactions

Response:

{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "read:accounts write:transactions"
}

Using Refresh Tokens

Access tokens expire after 1 hour. Use a refresh token to get a new access token:

POST /snapbooks/public/v2/oauth/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic {base64(client_id:client_secret)}

grant_type=refresh_token&
refresh_token={refresh_token}

Response:

{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "def50200ca01d5cf123b6ba9c5f2a...", // New refresh token
  "scope": "read:accounts write:transactions"
}

Note: Snapbooks uses token rotation for security. Each time you use a refresh token, you’ll receive a new one, and the old one becomes invalid.

Revoking Tokens

When a user logs out or you no longer need access:

POST /snapbooks/public/v2/oauth/revoke
Content-Type: application/x-www-form-urlencoded
Authorization: Basic {base64(client_id:client_secret)}

token={refresh_token}&
token_type_hint=refresh_token

Making Authenticated Requests

Use the access token for all API requests:

GET /snapbooks/api/v2/banking/accounts
Authorization: Bearer {access_token}

Error Responses

OAuth2 endpoints return standard error responses:

Error Description
invalid_request The request is missing a required parameter
invalid_client Client authentication failed
invalid_grant The provided authorization code or refresh token is invalid
unauthorized_client The client is not authorized to use the requested grant type
unsupported_grant_type The requested grant type is not supported
invalid_scope The requested scope is invalid or unknown
access_denied The user denied the authorization request

Example error response:

{
  "error": "invalid_grant",
  "error_description": "Refresh token expired"
}

Security Best Practices

  1. Store tokens securely:
    • Never store tokens in localStorage in browser apps
    • Use secure HTTP-only cookies or secure storage mechanisms
    • For mobile apps, use the platform’s secure storage (Keychain/Keystore)
  2. Implement proper PKCE for public clients (mobile/SPA apps)

  3. Always validate state parameters to prevent CSRF attacks

  4. Request minimal scopes - only ask for the permissions your app needs

  5. Implement token management:
    • Handle token expiration gracefully
    • Revoke tokens when a user logs out
    • Implement proper error handling for authentication failures
  6. Norwegian regulatory compliance:
    • Ensure GDPR compliance with proper user consent
    • Follow Datatilsynet guidelines for storing personal data
    • Implement appropriate logging for audit purposes according to Norwegian bookkeeping regulations

Relaterte artikler