# Authorization Code + PKCE Flow Template
OAuth 2.0 Authorization Code flow with Proof Key for Code Exchange (PKCE) for public clients.
## When to Use
- Single Page Applications (SPAs)
- Mobile native applications
- Desktop applications
- Any client that cannot securely store a client_secret
## Mermaid Template
```mermaid
%%{init: {'theme': 'base', 'themeVariables': {
'actorBkg': '#ddd6fe',
'actorTextColor': '#1f2937',
'actorBorder': '#6d28d9',
'signalColor': '#334155',
'signalTextColor': '#1f2937',
'noteBkgColor': '#f8fafc',
'noteTextColor': '#1f2937',
'noteBorderColor': '#334155'
}}}%%
sequenceDiagram
autonumber
box rgba(165,243,252,0.3) USER DEVICE
participant U as 👤 User
participant App as 📱 Mobile App
or SPA
end
box rgba(167,243,208,0.3) SALESFORCE
participant SF as ☁️ Salesforce
Authorization Server
end
Note over U,SF: Authorization Code + PKCE Flow (RFC 7636)
U->>App: 1. Tap "Login with Salesforce"
App->>App: 2. Generate PKCE Parameters
Note over App: code_verifier = random(128 bytes)
code_challenge = BASE64URL(SHA256(code_verifier))
App->>App: 3. Generate state (CSRF protection)
App->>SF: 4. Open browser/webview to /authorize
Note over App,SF: response_type=code
client_id=CONSUMER_KEY
redirect_uri=CALLBACK_URL
scope=api refresh_token
state=RANDOM_STATE
code_challenge=CODE_CHALLENGE
code_challenge_method=S256
SF->>U: 5. Display Login Page
U->>SF: 6. Enter Username & Password
SF->>SF: 7. Authenticate User
SF->>U: 8. Display Consent Screen
Note over SF,U: "App requests access to:
• API Access
• Refresh Token"
U->>SF: 9. Grant Consent (Allow)
SF->>SF: 10. Generate Authorization Code
Note over SF: Store code_challenge with code
SF->>App: 11. Redirect to callback with code
Note over SF,App: scheme://callback?code=AUTH_CODE
&state=RANDOM_STATE
App->>App: 12. Verify state matches
App->>SF: 13. POST /services/oauth2/token
Note over App,SF: grant_type=authorization_code
code=AUTH_CODE
client_id=CONSUMER_KEY
redirect_uri=CALLBACK_URL
code_verifier=CODE_VERIFIER
SF->>SF: 14. Validate Code & PKCE
Note over SF: Verify: BASE64URL(SHA256(code_verifier))
== stored code_challenge
SF->>App: 15. Return Tokens
Note over SF,App: {
access_token: "...",
refresh_token: "...",
instance_url: "https://...",
token_type: "Bearer"
}
App->>App: 16. Store tokens securely
Note over App: Use Keychain (iOS)
or Keystore (Android)
App->>U: 17. ✅ Successfully Logged In
```
## ASCII Fallback Template
```
┌──────────┐ ┌───────────────┐ ┌────────────────────┐
│ User │ │ Mobile App │ │ Salesforce │
│ │ │ or SPA │ │ (Auth Server) │
└────┬─────┘ └───────┬───────┘ └─────────┬──────────┘
│ │ │
│ 1. Tap Login │ │
│──────────────────>│ │
│ │ │
│ │ 2. Generate PKCE: │
│ │ code_verifier │
│ │ code_challenge │
│ │ │
│ 3. Open Browser/WebView to /authorize │
│<──────────────────│ │
│ │ │
│ 4. GET /authorize │
│ (client_id, code_challenge, state) │
│───────────────────────────────────────────────────────>│
│ │ │
│ 5. Login Page │
│<───────────────────────────────────────────────────────│
│ │ │
│ 6. Enter Credentials │
│───────────────────────────────────────────────────────>│
│ │ │
│ 7. Consent Screen │
│<───────────────────────────────────────────────────────│
│ │ │
│ 8. Grant Consent │
│───────────────────────────────────────────────────────>│
│ │ │
│ 9. Redirect with ?code=ABC123 │
│<───────────────────────────────────────────────────────│
│ │ │
│ 10. Deliver Code │ │
│──────────────────>│ │
│ │ │
│ │ 11. POST /token │
│ │ (code_verifier) │
│ │──────────────────────>│
│ │ │
│ │ 12. Verify PKCE: │
│ │ SHA256(verifier) │
│ │ == challenge │
│ │ │
│ │ 13. Access Token + │
│ │ Refresh Token │
│ │<──────────────────────│
│ │ │
│ 14. Logged In ✅ │ │
│<──────────────────│ │
```
## PKCE Parameter Generation
### JavaScript/TypeScript
```javascript
// Generate code_verifier (43-128 characters)
const codeVerifier = base64URLEncode(crypto.getRandomValues(new Uint8Array(32)));
// Generate code_challenge
const encoder = new TextEncoder();
const data = encoder.encode(codeVerifier);
const digest = await crypto.subtle.digest('SHA-256', data);
const codeChallenge = base64URLEncode(new Uint8Array(digest));
function base64URLEncode(buffer) {
return btoa(String.fromCharCode(...buffer))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
}
```
### Python
```python
import hashlib
import base64
import secrets
# Generate code_verifier
code_verifier = base64.urlsafe_b64encode(secrets.token_bytes(32)).rstrip(b'=').decode()
# Generate code_challenge
code_challenge = base64.urlsafe_b64encode(
hashlib.sha256(code_verifier.encode()).digest()
).rstrip(b'=').decode()
```
## Key Differences from Standard Auth Code
| Aspect | Standard | PKCE |
|--------|----------|------|
| Client Secret | Required | Not required |
| code_challenge | Not used | Required in /authorize |
| code_verifier | Not used | Required in /token |
| Security | Secret-based | Cryptographic proof |
| Client Type | Confidential | Public |
## Security Considerations
1. **Generate new PKCE values** for each authorization request
2. **Use S256 method** (SHA-256), never "plain"
3. **Store code_verifier securely** until token exchange
4. **Use secure storage** for tokens (Keychain/Keystore)
5. **Implement refresh token rotation** for additional security
## Customization Points
Replace these placeholders:
- `CONSUMER_KEY` → Your Connected App's Consumer Key
- `CALLBACK_URL` → Your registered callback URL (custom scheme for mobile)
- `CODE_VERIFIER` → Generated 43-128 character random string
- `CODE_CHALLENGE` → BASE64URL(SHA256(CODE_VERIFIER))