Dev.to Security 🔐 Cybersecurity 👁 0 📖 5 min read

OAuth 2.0 for Integrations: Scopes, Redirect URIs, and Tokens

OAuth 2.0 lets an application obtain limited access to a resource without asking the resource owner to share their password. A calendar integration, for example, can request permission to read events instead of receiving

OAuth 2.0 lets an application obtain limited access to a resource without asking the resource owner to share their password. A calendar integration, for example, can request permission to read events instead of receiving an account password with unrestricted access. But the security of that integration depends on the details: which permissions are requested, where the authorization response is sent, and how tokens are stored and renewed.

This guide walks through those decisions for teams building integrations. OAuth is an authorization framework; if you need to establish a user's identity, use an identity protocol such as OpenID Connect rather than assuming an access token is an identity document.

Start with the actors and the trust boundary

The resource owner grants access. The client is your application. The authorization server authenticates the user and issues credentials, while the resource server checks access tokens before serving protected data. Depending on the provider, these roles may belong to the same company, but the distinction helps when debugging failures.

Imagine a project management app that imports a user's calendar. The app sends the user to the provider's authorization endpoint. After the user authorizes the request, the provider redirects back to the app with an authorization code. The app exchanges that short-lived code at the token endpoint and uses the resulting access token to call the calendar API.

The application should not collect the user's provider password, put tokens into URLs, or treat a successful redirect by itself as proof that a user owns a specific account.

Use authorization code flow with PKCE

For interactive user authorization, use the authorization code flow with Proof Key for Code Exchange (PKCE), following the provider's documentation. PKCE binds the code exchange to a secret generated for that particular authorization attempt.

  1. Create a high-entropy, unpredictable code verifier.
  2. Derive its challenge with SHA-256 and the URL-safe base64 encoding required by the specification; send the challenge with method S256 in the authorization request.
  3. Store the verifier securely for that attempt. Send it to the token endpoint with the returned authorization code.
  4. Reject the response if the flow state cannot be matched to the initiating session.

PKCE reduces the usefulness of an intercepted authorization code. It does not make an unsafe redirect URI, leaked access token, or malicious dependency safe. Public clients such as browser and mobile apps cannot reliably keep a client secret; do not embed one and assume it is confidential.

Use a cryptographically random state value tied to the browser session where appropriate to protect the redirect flow against request forgery. Validate it on return and consume it once. OpenID Connect clients also need to validate the relevant identity-token claims and nonce according to that protocol.

Design scopes around actual tasks

A scope represents a permission requested from the authorization server. Names and semantics vary by provider, so read its documentation rather than assuming that similarly named scopes grant identical access.

List the operations the integration must perform: read calendar events, create a draft, or update an issue. Map each operation to the narrowest available scope. If the product initially only displays events, request read access, not edit and delete access. Explain the purpose in plain language before sending a user to the provider.

Request additional permissions when a user enables a feature that needs them, if the provider supports that approach. On the server, handle partial grants and denied consent gracefully: disable the affected feature and explain how to reconnect. Do not infer granted permissions solely from what was requested; use the provider's documented response or token metadata.

Periodically remove unused permissions. A smaller grant limits damage if a credential is compromised and makes consent easier for people to evaluate.

Treat redirect URIs as a security boundary

The redirect URI is where the authorization server sends the browser after consent. Register the exact callback URI required by your deployment and use it consistently in authorization and token requests. Modern OAuth security guidance favors exact matching, with narrowly defined exceptions such as localhost ports for native applications. Avoid wildcard domains and open redirectors.

For example, https://app.example.com/oauth/callback is a different destination from an attacker-controlled host that merely contains the same text. Check scheme, host, port and path. Use HTTPS for web callbacks and follow the provider's and native-app specifications for platform-specific redirect methods.

Do not accept an arbitrary return URL from a query parameter and send the authorization code there. Instead, keep an allowlisted destination or a server-side mapping keyed to the flow state. Handle errors as carefully as successful callbacks: validate the flow state, show a useful message, and avoid exposing sensitive values in logs.

Store, use and rotate tokens deliberately

Access tokens are credentials for calling a resource server. Send them over TLS using the documented authorization mechanism, normally the Bearer authorization header. Avoid storing them in source code, analytics events, browser history or application logs. Limit access to stored credentials, encrypt sensitive data where appropriate, and design token storage around the threat model of the client.

A refresh token, when issued, can obtain new access tokens without repeating the consent flow. Treat it as particularly sensitive. If the provider uses refresh token rotation, save the replacement safely and discard the previous token according to its documented rules. Concurrent refresh requests can race: coordinate refresh per account so two workers do not overwrite each other's token state.

Account for expiry and revocation. A failed refresh should move the integration into a reconnect state rather than trigger an endless retry loop. Provide a disconnect action that removes local credentials and, when supported, calls the provider's revocation endpoint. Never print full tokens in error reports; a correlation ID is more useful for support.

A practical pre-launch check

Test the flow with a new account and with a user who denies consent. Verify that changing the state causes rejection, the code cannot be reused, and an unregistered redirect URI is refused. Test expired access tokens, revoked grants, simultaneous refresh attempts and the reconnect path. Inspect logs and error monitoring for codes, tokens and client secrets.

Document which scopes each feature needs, who can access stored credentials, how to revoke a compromised integration and how to rotate the client secret for a confidential client. Revisit these choices whenever a new provider or feature is added.

For the normative details, consult RFC 6749, PKCE in RFC 7636, and OAuth 2.0 Security Best Current Practice, RFC 9700.

Originally published in Russian: https://morlix.ru/blog/oauth-2-dlya-integratsiy-scopes-redirect-uri-i-tokeny

Original article (Russian)

📰 Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.