--- sidebar_position: 5 --- import OAuthExamples from "../snippets/oauth-examples.mdx" # OAuth Flow Quickstart This guide provides a complete walkthrough with working code examples for implementing the OAuth flow. For a conceptual overview of how OAuth works, see [Getting Credentials via OAuth][oauth-credentials]. ## Prerequisites Before you begin, ensure you have: - **Your own integrator service account credentials** and an authenticated noon session for your backend. If you have not set this up yet, start with [Getting Your Credentials][getting-credentials] and [Authenticating Your Requests][authenticating-requests]. - **OAuth client credentials** (client_id and client_secret) provided by noon - At least one **redirect URI** registered with noon for receiving authorization codes. A client can have several — see [Choosing a Callback URL][oauth-callback-url] - Basic understanding of OAuth 2.0 authorization code flow :::warning Use your integrator authentication for the OAuth API calls When your backend calls [`POST /identity/oauth/v1/token/create`][oauth-create-token-api] and [`POST /identity/oauth/v1/token/exchange`][oauth-exchange-token-api], those requests must be sent with an authenticated noon session created from **your integrator service account credentials**. The OAuth `client_id` and `client_secret` identify your application during the code exchange, but they do not make the request authenticated on their own. The seller-scoped credential is returned only after the exchange succeeds. ::: ## Implementation Guide ### Step 1: Redirect User to Authorization URL When a seller wants to connect their noon account to your platform, generate a PKCE `code_verifier`, derive its `code_challenge`, store the verifier server-side, then redirect them to the noon authorization endpoint: ``` https://oauth.noon.partners/?client_id=abc123xyz&state=random-state-string-123&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256 ``` **Query Parameters:** | Parameter | Description | Example | |-----------|-------------|---------| | `client_id` | Your OAuth client ID | `abc123xyz` | | `state` | Random string for CSRF protection | `random-state-string-123` | | `code_challenge` | Base64url, unpadded, of `SHA-256(code_verifier)` | `E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM` | | `code_challenge_method` | Must be `S256` | `S256` | | `redirect_uri` | Optional. Which registered callback URL to return to. Omitted, the first one registered is used | `https://yourapp.com/callback` | :::info The seller will be presented with a consent screen showing what permissions your application is requesting. They must approve before proceeding. ::: :::warning PKCE may be mandatory for your client `code_challenge` and `code_challenge_method` are required whenever your OAuth client is configured to require PKCE — the seller will see an error on the consent screen if they are missing. For other clients they are optional per flow, but recommended. Full details, including how to generate the verifier, are in [Securing the Flow with PKCE][oauth-pkce]. Whichever applies, store the `code_verifier` alongside `state` in your server-side session record for this flow. You need it in Step 3, and it must never be sent to the browser. ::: ### Step 2: Handle Authorization Callback After the seller approves, noon redirects them back to your registered redirect URI with the authorization code: ``` https://yourapp.com/callback?code=AUTH_CODE_HERE&state=random-state-string-123&iss=https%3A%2F%2Foauth.noon.partners ``` Your callback handler must: 1. **Verify the state parameter** matches what you sent to prevent CSRF attacks 2. **Extract the authorization code** from the `code` parameter 3. **Load the `code_verifier`** you stored for this flow, if you started the flow with a `code_challenge` 4. **Ignore, or optionally check, `iss`** — it names the authorization server that issued the code (`https://oauth.noon.partners`). Do not treat it as an unexpected parameter and fail **Example callback handling:** ```python from flask import Flask, request, redirect app = Flask(__name__) @app.route('/callback') def oauth_callback(): # Verify state parameter state = request.args.get('state') if state != session.get('oauth_state'): # session refers to any server-side storage you use to track state for that user request return "Invalid state parameter", 400 # Optional: confirm which authorization server issued this code (RFC 9207) issuer = request.args.get('iss') if issuer and issuer != 'https://oauth.noon.partners': return "Unexpected issuer", 400 # Get authorization code auth_code = request.args.get('code') if not auth_code: return "No authorization code received", 400 # Proceed to exchange code for token return exchange_code_for_token(auth_code) ``` ### Step 3: Exchange Authorization Code for Access Token Make a server-to-server API call to exchange the authorization code for an access token. Use the same authenticated integrator session for this request. **Endpoint:** [`POST /identity/oauth/v1/token/create`][oauth-create-token-api] **Request Body:** ```json { "grant_type": "authorization_code", "code": "AUTH_CODE_HERE", "client_id": "your_client_id", "client_secret": "your_client_secret", "code_verifier": "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk" } ``` Include `code_verifier` when this flow's authorization URL carried a `code_challenge`, and send the verifier matching that exact challenge. Omit it entirely for flows that did not use PKCE — sending one for a non-PKCE flow is rejected. See [Securing the Flow with PKCE][oauth-pkce]. **Response:** ```json { "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...", "token_type": "TOKEN_TYPE_BEARER", "expires_in": "3600s", "scopes": ["access:grant"], "project_code": "PRJ12345" } ``` :::warning Security Best Practice **Never expose your client_secret** in client-side code. This exchange must happen on your backend server. ::: **Important Response Fields:** - `access_token`: JWT token needed for the next step (valid for 1 hour and single-use) - `project_code`: The seller's project code to which the service account will have access - `expires_in`: Token validity duration ### Step 4: Create Service Account and Receive Credentials Finally, use the access token to create the service account and receive its credentials. This request must also reuse your authenticated integrator session. **Endpoint:** [`POST /identity/oauth/v1/token/exchange`][oauth-exchange-token-api] **Request Body:** ```json { "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." } ``` **Response:** ```json { "status": { "code": 0 }, "project_code": "PRJ12345", "oauth_request_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "result": { "key_id": "key-abc123", "private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQ...", "channel_identifier": "my-integrator-prj12345-abc@p99999.idp.noon.partners", "project_code": "PRJ12345", "type": "apijwt", "issued_at": "2026-04-20T12:00:00Z" } } ``` **Important Response Fields:** - `status`: Indicates whether the workflow executed successfully (`code: 0` means success) - `project_code`: The seller's project code - `oauth_request_id`: A unique identifier for tracking this OAuth exchange request — store this to monitor the service account creation progress - `result`: The service account credentials, including the private key needed to authenticate API calls :::warning Store Your Credentials Securely The `result` contains the **private key**, which is only returned once — noon does not store it. Store it in a secrets manager immediately. If you lose it, you can create a new credential via the [API User Service][apiuser-managing-credentials]. ::: :::tip Tracking Your Request Save the `oauth_request_id` from the response. You can use it to check the status of the request in the **OAuth** tab of the [noon Partners Access App][partner-api-access]. This is helpful for debugging or confirming that the workflow completed successfully. ::: ## Complete Code Examples Here are complete, working examples in multiple programming languages: ## Testing Your Integration ### Verification Checklist Before going live with sellers, verify: - [ ] State parameter is validated to prevent CSRF - [ ] A fresh `code_verifier` is generated per flow and never reused - [ ] The `code_verifier` is stored server-side and never reaches the browser - [ ] The `code_verifier` sent at token creation belongs to the same flow as the authorization code - [ ] Authorization codes are used only once - [ ] Access tokens are stored securely and never logged - [ ] Token expiry is handled gracefully - [ ] Client secret is never exposed to clients - [ ] Error responses are handled appropriately ## Security Best Practices For comprehensive security guidelines, see the [Security Considerations][oauth-security] in the Getting Credentials via OAuth guide. ### 1. Protect Your Client Secret ```python # BAD - Hardcoded secret client_secret = "my-secret-123" # GOOD - Use environment variables import os client_secret = os.environ.get('NOON_CLIENT_SECRET') ``` ### 2. Validate State Parameter ```python # Always validate state import secrets # Generate random state state = secrets.token_urlsafe(32) session['oauth_state'] = state # Later, in callback if request.args.get('state') != session.get('oauth_state'): raise ValueError("CSRF detected") ``` ### 3. Generate a Fresh PKCE Verifier Per Flow ```python import base64 import hashlib import os def generate_pkce_pair(): # 32 random bytes -> 43-character base64url verifier, no padding verifier = base64.urlsafe_b64encode(os.urandom(32)).decode('ascii').rstrip('=') digest = hashlib.sha256(verifier.encode('ascii')).digest() challenge = base64.urlsafe_b64encode(digest).decode('ascii').rstrip('=') return verifier, challenge # Keep the verifier server-side, next to the state you already store verifier, challenge = generate_pkce_pair() session['code_verifier'] = verifier # Only the challenge goes on the authorization URL ``` ### 4. Use HTTPS Only ```python # Enforce HTTPS in production if not request.is_secure and app.env == 'production': return redirect(request.url.replace('http://', 'https://')) ``` ### 5. Use Access Tokens Immediately ```python # Exchange access token immediately after receiving it # Access tokens are single-use and should be consumed right away token_response = get_access_token(auth_code) access_token = token_response['access_token'] # Immediately exchange for service account creation sa_response = create_service_account(access_token) ``` ## Next Steps Now that you've completed the OAuth flow: 1. **Store the credentials from `result` securely** — Save the private key in a secrets manager and associate it with the seller in your database 2. **Start making API calls** — Use the credentials as shown in the [Authentication Guide][authenticating-requests] 3. **Rotate or revoke credentials as needed** — Use the [API User Service][apiuser-managing-credentials] to manage credentials over time ## Additional Resources - [OAuth API Reference][oauth-api-reference] - Complete API documentation - [Getting Credentials via OAuth][oauth-credentials] - Detailed OAuth concepts ## Need Help? If you encounter issues: - Review the error messages in the API response - Check the [Error Handling][oauth-errors] section for common issues - Contact Support with your client_id (never share client_secret)