OAuth

OAuth

OAuth allows external applications to access the Chariot API on behalf of a nonprofit organization

OAuth access is not self-service. Contact support@givechariot.com to register your application and receive client credentials. Include your application’s display name, logo, callback URLs, and optionally the IP addresses to whitelist.

How It Works

Chariot implements the OAuth 2.0 Authorization Code flow with PKCE (Proof Key for Code Exchange):

  1. Your application generates a PKCE code verifier and code challenge
  2. Your application redirects a nonprofit user to Chariot’s authorization page with the code challenge
  3. The user reviews the requested permissions and approves access
  4. Chariot redirects back to your application with an authorization code
  5. Your server exchanges the code and the code verifier for access and refresh tokens
  6. You use the access token to call the Chariot API on behalf of the organization

Authentication

When Chariot registers your application, you’ll receive a client_id and client_secret. Your server uses these credentials (via HTTP Basic Auth) to authenticate with Chariot during the OAuth token exchange and refresh flows. Once you have an access token, your server uses that token — not the client credentials — to call the Chariot API on behalf of the nonprofit.

Your client secret should be kept secure and should never be exposed to the public.

Environments

EndpointProductionSandbox
Authorizationhttps://dashboard.givechariot.com/oauth/authorizehttps://dashboard.givechariot.com/oauth/sandbox/authorize
Tokenhttps://api.givechariot.com/auth/oauth/tokenhttps://sandboxapi.givechariot.com/auth/oauth/token
OIDC configurationhttps://api.givechariot.com/.well-known/openid-configurationhttps://sandboxapi.givechariot.com/.well-known/openid-configuration

Note that each environment issues its own client_id and client_secret. This means that sandbox credentials cannot be used in production and vice versa.

Additional OAuth 2.0 and OIDC metadata can be found at the environment’s well-known discovery document.

Scopes

Scopes define what your application can access. Specify multiple as a URL encoded space-separated string when requesting authorization. The scopes your client is allowed to request are fixed during client registration.

ScopeGrants
read_onlyRead access across every resource in the Chariot API
read_writeRead and write access across every resource in the Chariot API
openidIssue an id_token and access the UserInfo endpoint. See Identity
offline_accessAccepted for compatibility with OIDC clients

read_only and read_write are aggregates — they grant their level of access across every resource, so you do not have to enumerate a scope per resource or request new ones as the API grows. Neither grants Chariot’s internal administrative operations.

A token only receives the permissions your client, the authorization, and the consenting user’s role all have in common. Request read_only alongside read_write if you serve nonprofits whose users hold read-only roles — a read-only user cannot approve a request for read_write alone.

Authorization Code Flow

Step 1: Generate a PKCE Code Verifier and Challenge

Before redirecting the user, generate a cryptographically random code verifier (43-128 characters, using A-Z, a-z, 0-9, -, ., _, ~), then compute its SHA-256 hash and base64url-encode it to create the code challenge:

1const crypto = require("crypto");
2
3function generatePKCE() {
4 const codeVerifier = crypto.randomBytes(32).toString("base64url");
5 const codeChallenge = crypto
6 .createHash("sha256")
7 .update(codeVerifier)
8 .digest("base64url");
9 return { codeVerifier, codeChallenge };
10}

Store the codeVerifier — you’ll need it in Step 5.

Step 2: Redirect to Chariot

Direct the user’s browser to Chariot’s authorization endpoint:

GET https://dashboard.givechariot.com/oauth/authorize
ParameterRequiredDescription
client_idYesYour application’s client ID
redirect_uriYesURL to redirect back to (must match a registered URI). See Redirect URI Requirements
response_typeYesMust be code
scopeYesSpace-separated list of requested scopes, percent-encoded (%20) as a single query parameter value
stateRecommendedAn opaque value to prevent CSRF attacks
nonceYesCryptographically random string to prevent replay attacks. See Identity
code_challengeYesBase64url-encoded SHA-256 hash of the code verifier
code_challenge_methodYesMust be S256

Example:

https://dashboard.givechariot.com/oauth/authorize
?client_id=YOUR_CLIENT_ID
&redirect_uri=https://example.com/callback
&response_type=code
&scope=openid%20read_write
&state=RANDOM_CSRF_TOKEN
&code_challenge=YOUR_CODE_CHALLENGE
&code_challenge_method=S256

Chariot presents the nonprofit user with a consent screen showing your application name, logo, and the permissions being requested.

Step 4: Receive the Authorization Code

After approval, Chariot redirects back to your redirect_uri:

https://example.com/callback?code=AUTHORIZATION_CODE&state=RANDOM_CSRF_TOKEN

Always verify that the returned state matches the value you sent in Step 2 to prevent CSRF attacks.

Authorization codes expire shortly after they were issued and can only be used once.

Step 5: Exchange the Code for Tokens

Make a server-side POST request to the token endpoint, authenticating with HTTP Basic Auth. Include the code_verifier you generated in Step 1:

$curl -X POST https://api.givechariot.com/auth/oauth/token \
> -u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" \
> -d "grant_type=authorization_code" \
> -d "code=<AUTHORIZATION_CODE>" \
> -d "code_verifier=<YOUR_CODE_VERIFIER>"

Response:

1{
2 "access_token": "ACCESS_TOKEN",
3 "refresh_token": "REFRESH_TOKEN",
4 "expires_in": 900,
5 "token_type": "Bearer"
6}

Using Access Tokens

Include the access token in the Authorization header of your API requests:

$curl https://api.givechariot.com/v1/donations \
> -H "Authorization: Bearer ACCESS_TOKEN"

Access tokens expire after 15 minutes. Use the refresh token to obtain a new one.

Refreshing Tokens

When an access token expires, exchange your refresh token for a new token pair.

Refresh tokens are single-use. After a successful exchange, you must use the new refresh token returned in the response. Reusing a previously exchanged refresh token may result in authorization revocation, requiring the nonprofit to re-authorize your application.

$curl -X POST https://api.givechariot.com/auth/oauth/token \
> -u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" \
> -d "grant_type=refresh_token" \
> -d "refresh_token=REFRESH_TOKEN"

Response:

1{
2 "access_token": "NEW_ACCESS_TOKEN",
3 "refresh_token": "NEW_REFRESH_TOKEN",
4 "expires_in": 900,
5 "token_type": "Bearer"
6}

Refresh tokens use a sliding window expiration:

  • Each use extends the refresh token’s lifetime by 31 days
  • The absolute maximum lifetime is 365 days, and refreshing does not extend it
  • After 365 days, the user must re-authorize your application

Token Lifetimes

TokenLifetime
Authorization code1 minute
Access token15 minutes
Refresh token31 days (sliding), 365 days max

Error Handling

Common errors during the token exchange:

HTTP StatusCause
400Malformed request (invalid parameters, missing fields)
401Invalid authorization code, expired token, or bad client credentials
412User lacks the required permissions for the requested scopes

IP Whitelisting

Some APIs restrict access to a set of registered IP addresses in addition to OAuth. Where this applies, the check runs before the token is validated — a request from an unregistered address receives a 403 Forbidden even with a valid access token:

1{
2 "type": "about:blank",
3 "title": "You have insufficient permissions to perform this action.",
4 "status": 403,
5 "detail": "Your IP address is not in the allowed CIDR range to access this resource."
6}

Addresses are registered as CIDR ranges when your application is set up. Contact support@givechariot.com to add or change them.

Redirect URI Requirements

Redirect URIs are validated when your application is registered and again at authorization time. Each URI must meet the following requirements:

RuleRequirement
SchemeMust use https
HostMust include a hostname
PathMust include a path beyond / (e.g., /callback)
No query parametersQuery strings are not allowed
No fragmentsURL fragments (#) are not allowed
No user infoCredentials in the URL (e.g., user:pass@host) are not allowed
LengthMust be 255 characters or fewer
Max countYou can register up to 5 redirect URIs per client

At authorization time, the redirect_uri parameter must exactly match one of your registered URIs. Scheme and host are compared case-insensitively, but the path must match exactly. Wildcard or pattern matching is not supported.

Valid examples:

https://example.com/callback
https://example.com/auth/chariot/callback
https://example.com:443/callback

Invalid examples:

http://example.com/callback # HTTP not allowed in production
https://example.com # Missing path
https://example.com/ # Root path not allowed
https://example.com/cb?foo=bar # Query parameters not allowed