OAuth2 PKCE
Makenot.work supports OAuth2 Authorization Code with PKCE for “Log in with Makenot.work” flows. This lets third-party applications authenticate users without handling their passwords directly.
Overview
- Your app generates a PKCE code verifier and challenge
- User is redirected to
makenot.work/oauth/authorizeto log in and consent - Makenot.work redirects back with an authorization code
- Your app exchanges the code for a JWT access token
- Use the token to call SyncKit or userinfo endpoints
Client Registration
Your OAuth client ID is the API key of your SyncKit app. Create a SyncKit app from the Makenot.work dashboard to get one.
Redirect URIs
Localhost: http://127.0.0.1:{port}/... and http://localhost:{port}/... are always allowed without registration. Use these for desktop apps.
Remote: Non-localhost redirect URIs must be registered on your SyncKit app. Contact support to add them.
Authorization Request
Redirect the user to the authorize endpoint:
GET /oauth/authorize
?response_type=code
&client_id=<your-api-key>
&redirect_uri=http://127.0.0.1:8765/callback
&state=<random-string>
&code_challenge=<S256-challenge>
&code_challenge_method=S256
| Parameter | Required | Description |
|---|---|---|
response_type | Yes | Must be code |
client_id | Yes | Your SyncKit app API key |
redirect_uri | Yes | Where to send the authorization code |
state | Yes | Random string to prevent CSRF; verify it in the callback |
code_challenge | Yes | Base64url-encoded SHA-256 hash of the code verifier |
code_challenge_method | Yes | Must be S256 |
The user sees a consent page. After logging in and approving, they are redirected to:
{redirect_uri}?code=<authorization-code>&state=<your-state>
Token Exchange
Exchange the authorization code for an access token:
POST /oauth/token
Content-Type: application/json
{
"grant_type": "authorization_code",
"code": "<authorization-code>",
"redirect_uri": "http://127.0.0.1:8765/callback",
"code_verifier": "<original-code-verifier>",
"client_id": "<your-api-key>"
}
Response:
{
"access_token": "eyJ...",
"token_type": "Bearer",
"expires_in": 604800,
"user_id": "550e8400-...",
"app_id": "660f9500-..."
}
The authorization code is single-use. The server verifies SHA256(code_verifier) == code_challenge before issuing a token.
User Info
Retrieve the authenticated user’s profile:
GET /oauth/userinfo
Authorization: Bearer <access_token>
Response:
{
"user_id": "550e8400-...",
"username": "alice",
"display_name": "Alice",
"avatar_url": "https://makenot.work/static/avatars/alice.jpg"
}
PKCE Implementation
PKCE prevents authorization code interception:
- Generate a random code verifier (43-128 characters, URL-safe)
- Compute
code_challenge = BASE64URL(SHA256(code_verifier)) - Send
code_challengein the authorization request - Send
code_verifierin the token exchange
The server rejects token requests where the verifier does not match the challenge.
Example (Rust)
use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine};
use sha2::{Digest, Sha256};
let verifier: String = (0..64)
.map(|_| rand::random::<u8>())
.map(|b| format!("{:02x}", b))
.collect();
let challenge = URL_SAFE_NO_PAD.encode(Sha256::digest(verifier.as_bytes()));
Token Usage
The access token works with all SyncKit endpoints:
- Cloud Sync: push/pull data, manage devices
- OTA Updates: manage releases and artifacts
- User info (above)
Tokens expire after 7 days. After expiration, redirect the user through the authorization flow again.
Error Handling
Authorization errors redirect to redirect_uri with an error parameter:
{redirect_uri}?error=access_denied&state=<your-state>
Token exchange errors return JSON:
{
"error": "invalid_grant"
}
| Error | Meaning |
|---|---|
access_denied | User denied consent |
invalid_client | Unknown client_id |
invalid_grant | Code expired, already used, or verifier mismatch |
invalid_request | Missing required parameters |
See Also
- API Overview: authentication methods and rate limits
- SyncKit Cloud Sync: using the token for data sync