# 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 1. Your app generates a PKCE code verifier and challenge 2. User is redirected to `makenot.work/oauth/authorize` to log in and consent 3. Makenot.work redirects back with an authorization code 4. Your app exchanges the code for a JWT access token 5. 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. Email info@makenot.work to add them. ## Authorization Request Redirect the user to the authorize endpoint: ``` GET /oauth/authorize ?response_type=code &client_id= &redirect_uri=http://127.0.0.1:8765/callback &state= &code_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=&state= ``` ## Token Exchange Exchange the authorization code for an access token: ``` POST /oauth/token Content-Type: application/json { "grant_type": "authorization_code", "code": "", "redirect_uri": "http://127.0.0.1:8765/callback", "code_verifier": "", "client_id": "" } ``` Response: ```json { "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 ``` Response: ```json { "user_id": "550e8400-...", "username": "alice", "display_name": "Alice", "avatar_url": "https://makenot.work/static/avatars/alice.jpg" } ``` ## PKCE Implementation PKCE prevents authorization code interception: 1. Generate a random code verifier (43-128 characters, URL-safe) 2. Compute `code_challenge = BASE64URL(SHA256(code_verifier))` 3. Send `code_challenge` in the authorization request 4. Send `code_verifier` in the token exchange The server rejects token requests where the verifier does not match the challenge. ### Example (Rust) ```rust use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine}; use sha2::{Digest, Sha256}; let verifier: String = (0..64) .map(|_| rand::random::()) .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](./synckit.md): push/pull data, manage devices - [OTA Updates](./ota.md): 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= ``` Token exchange errors return JSON: ```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](./api-overview.md): authentication methods and rate limits - [SyncKit Cloud Sync](./synckit.md): using the token for data sync