|
| 1 | +--- |
| 2 | +title: OAuth 2.0 Implementation Guide |
| 3 | +sidebar_label: OAuth Setup |
| 4 | +slug: oauth-setup |
| 5 | +excerpt: Complete guide to implementing OAuth 2.0 with RevenueCat's authorization server |
| 6 | +--- |
| 7 | + |
| 8 | +This guide walks you through implementing OAuth 2.0 authorization with RevenueCat's API. |
| 9 | + |
| 10 | +## Client Registration |
| 11 | + |
| 12 | +To integrate with RevenueCat's OAuth server, you'll need to register your application as an OAuth client. Contact our [support team ](mailto:[email protected]) to register your client with the following information: |
| 13 | + |
| 14 | +- **Client Name**: Display name for your application |
| 15 | +- **Client URI**: Your application's homepage URL |
| 16 | +- **Redirect URIs**: Valid callback URLs for your application |
| 17 | +- **Client Type**: Public (for native/desktop apps) or Confidential (for server-side apps) |
| 18 | + |
| 19 | +## Authorization Flow |
| 20 | + |
| 21 | +### Step 1: Initiate Authorization |
| 22 | + |
| 23 | +Direct users to the authorization endpoint: |
| 24 | + |
| 25 | +``` |
| 26 | +GET https://api.revenuecat.com/oauth2/authorize |
| 27 | +``` |
| 28 | + |
| 29 | +**Required Parameters:** |
| 30 | + |
| 31 | +- `client_id`: Your client identifier |
| 32 | +- `response_type`: Must be `code` |
| 33 | +- `redirect_uri`: Must match a registered redirect URI |
| 34 | +- `scope`: Space-separated list of requested permissions |
| 35 | +- `code_challenge`: PKCE code challenge (required for public clients) |
| 36 | +- `code_challenge_method`: Must be `S256` (required for public clients) |
| 37 | + |
| 38 | +**Optional Parameters:** |
| 39 | + |
| 40 | +- `state`: Random string to prevent CSRF attacks (recommended) |
| 41 | + |
| 42 | +**Example Authorization URL:** |
| 43 | + |
| 44 | +```url |
| 45 | +https://api.revenuecat.com/oauth2/authorize? |
| 46 | + client_id=your_client_id& |
| 47 | + response_type=code& |
| 48 | + redirect_uri=https://yourapp.com/callback& |
| 49 | + scope=project_configuration:apps:read& |
| 50 | + state=random_state_string& |
| 51 | + code_challenge=your_code_challenge& |
| 52 | + code_challenge_method=S256 |
| 53 | +``` |
| 54 | + |
| 55 | +### Step 2: Handle Authorization Response |
| 56 | + |
| 57 | +After the user grants permission, they'll be redirected to your `redirect_uri`. |
| 58 | + |
| 59 | +**Success Response:** |
| 60 | + |
| 61 | +``` |
| 62 | +https://yourapp.com/callback?code=authorization_code&state=random_state_string |
| 63 | +``` |
| 64 | + |
| 65 | +**Error Response:** |
| 66 | + |
| 67 | +``` |
| 68 | +https://yourapp.com/callback?error=access_denied&error_description=description&state=random_state_string |
| 69 | +``` |
| 70 | + |
| 71 | +### Step 3: Exchange Code for Tokens |
| 72 | + |
| 73 | +Exchange the authorization code for access and refresh tokens: |
| 74 | + |
| 75 | +```bash |
| 76 | +curl -X POST https://api.revenuecat.com/oauth2/token \ |
| 77 | + -H "Content-Type: application/x-www-form-urlencoded" \ |
| 78 | + -d "grant_type=authorization_code&code=your_auth_code&redirect_uri=https://yourapp.com/callback&client_id=your_client_id&client_secret=your_client_secret" |
| 79 | +``` |
| 80 | + |
| 81 | +**Parameters:** |
| 82 | + |
| 83 | +- `grant_type`: Must be `authorization_code` |
| 84 | +- `code`: The authorization code from Step 2 |
| 85 | +- `redirect_uri`: Must match the redirect URI from Step 1 |
| 86 | +- `client_id`: Your client identifier |
| 87 | +- `client_secret`: Your client secret (required for confidential clients) |
| 88 | +- `code_verifier`: PKCE code verifier (required for public clients) |
| 89 | + |
| 90 | +**Success Response:** |
| 91 | + |
| 92 | +```json |
| 93 | +{ |
| 94 | + "access_token": "atk_...", |
| 95 | + "token_type": "Bearer", |
| 96 | + "expires_in": 3600, |
| 97 | + "refresh_token": "rtk_...", |
| 98 | + "scope": "project_configuration:apps:read" |
| 99 | +} |
| 100 | +``` |
| 101 | + |
| 102 | +## Token Management |
| 103 | + |
| 104 | +### Access Tokens |
| 105 | + |
| 106 | +- **Lifetime**: 1 hour |
| 107 | +- **Usage**: Include in API requests via `Authorization: Bearer {access_token}` header |
| 108 | +- **Prefix**: `atk_` |
| 109 | + |
| 110 | +### Refresh Tokens |
| 111 | + |
| 112 | +- **Lifetime**: 1 month |
| 113 | +- **Usage**: Exchange for new access tokens when they expire |
| 114 | +- **Prefix**: `rtk_` |
| 115 | + |
| 116 | +### Refreshing Tokens |
| 117 | + |
| 118 | +When your access token expires, use the refresh token to get a new pair: |
| 119 | + |
| 120 | +```bash |
| 121 | +curl -X POST https://api.revenuecat.com/oauth2/token \ |
| 122 | + -H "Content-Type: application/x-www-form-urlencoded" \ |
| 123 | + -d "grant_type=refresh_token&refresh_token=your_refresh_token&client_id=your_client_id&client_secret=your_client_secret" |
| 124 | +``` |
| 125 | + |
| 126 | +**Parameters:** |
| 127 | + |
| 128 | +- `grant_type`: Must be `refresh_token` |
| 129 | +- `refresh_token`: Your current refresh token |
| 130 | +- `client_id`: Your client identifier |
| 131 | +- `client_secret`: Your client secret (required for confidential clients) |
| 132 | + |
| 133 | +:::warning Token Rotation |
| 134 | +When tokens are refreshed, both the old access and refresh tokens are revoked, and new ones are issued. Make sure to update your stored tokens. |
| 135 | +::: |
| 136 | + |
| 137 | +## Available Scopes |
| 138 | + |
| 139 | +Request only the scopes your application needs: |
| 140 | + |
| 141 | +### Project Configuration |
| 142 | + |
| 143 | +- `project_configuration:projects:read` - List projects |
| 144 | +- `project_configuration:apps:read` - Read app information |
| 145 | +- `project_configuration:apps:read_write` - Create, update, delete apps |
| 146 | +- `project_configuration:entitlements:read` - Read entitlements |
| 147 | +- `project_configuration:entitlements:read_write` - Manage entitlements |
| 148 | +- `project_configuration:offerings:read` - Read offerings |
| 149 | +- `project_configuration:offerings:read_write` - Manage offerings |
| 150 | +- `project_configuration:packages:read` - Read packages |
| 151 | +- `project_configuration:packages:read_write` - Manage packages |
| 152 | +- `project_configuration:products:read` - Read products |
| 153 | +- `project_configuration:products:read_write` - Manage products |
| 154 | + |
| 155 | +## Making API Requests |
| 156 | + |
| 157 | +Include the access token in the `Authorization` header: |
| 158 | + |
| 159 | +```bash |
| 160 | +curl -H "Authorization: Bearer atk_your_access_token" \ |
| 161 | + https://api.revenuecat.com/v2/projects |
| 162 | +``` |
| 163 | + |
| 164 | +## PKCE Implementation |
| 165 | + |
| 166 | +For public clients, implement PKCE (Proof Key for Code Exchange) to enhance security: |
| 167 | + |
| 168 | +### 1. Generate Code Verifier and Challenge |
| 169 | + |
| 170 | +```javascript |
| 171 | +// Generate a random code verifier (43-128 characters) |
| 172 | +function generateCodeVerifier() { |
| 173 | + const array = new Uint8Array(32); |
| 174 | + crypto.getRandomValues(array); |
| 175 | + return base64URLEncode(array); |
| 176 | +} |
| 177 | + |
| 178 | +// Create code challenge from verifier |
| 179 | +async function generateCodeChallenge(verifier) { |
| 180 | + const encoder = new TextEncoder(); |
| 181 | + const data = encoder.encode(verifier); |
| 182 | + const digest = await crypto.subtle.digest("SHA-256", data); |
| 183 | + return base64URLEncode(new Uint8Array(digest)); |
| 184 | +} |
| 185 | + |
| 186 | +// Base64 URL encoding helper |
| 187 | +function base64URLEncode(str) { |
| 188 | + return btoa(String.fromCharCode.apply(null, str)) |
| 189 | + .replace(/\+/g, "-") |
| 190 | + .replace(/\//g, "_") |
| 191 | + .replace(/=/g, ""); |
| 192 | +} |
| 193 | +``` |
| 194 | + |
| 195 | +### 2. Use in Authorization Request |
| 196 | + |
| 197 | +Include `code_challenge` and `code_challenge_method=S256` in your authorization URL. |
| 198 | + |
| 199 | +### 3. Include in Token Exchange |
| 200 | + |
| 201 | +Send the original `code_verifier` when exchanging the authorization code for tokens. |
| 202 | + |
| 203 | +## Error Handling |
| 204 | + |
| 205 | +### Authorization Errors |
| 206 | + |
| 207 | +- `invalid_request` - Missing or invalid parameters |
| 208 | +- `unauthorized_client` - Client not authorized for this grant type |
| 209 | +- `access_denied` - User denied authorization |
| 210 | +- `unsupported_response_type` - Invalid response type |
| 211 | +- `invalid_scope` - Requested scope is invalid or unknown |
| 212 | +- `server_error` - Internal server error |
| 213 | + |
| 214 | +### Token Errors |
| 215 | + |
| 216 | +- `invalid_request` - Missing or invalid parameters |
| 217 | +- `invalid_client` - Client authentication failed |
| 218 | +- `invalid_grant` - Authorization code/refresh token is invalid or expired |
| 219 | +- `unauthorized_client` - Client not authorized for this grant type |
| 220 | +- `unsupported_grant_type` - Grant type not supported |
| 221 | + |
| 222 | +## Best Practices |
| 223 | + |
| 224 | +1. **Store tokens securely** - Never expose tokens in client-side code |
| 225 | +2. **Implement proper error handling** - Handle token expiration gracefully |
| 226 | +3. **Use HTTPS only** - All OAuth flows must use secure connections |
| 227 | +4. **Validate state parameter** - Prevent CSRF attacks |
| 228 | +5. **Request minimal scopes** - Only request permissions you actually need |
| 229 | +6. **Implement token refresh** - Handle access token expiration automatically |
| 230 | + |
| 231 | +## Rate Limits |
| 232 | + |
| 233 | +OAuth tokens are subject to the same rate limits as API keys. Monitor your usage and implement appropriate backoff strategies. |
| 234 | + |
| 235 | +## Support |
| 236 | + |
| 237 | +For questions about OAuth integration or to register your client, contact our support team at [[email protected]](mailto:[email protected]). |
0 commit comments