Integrate Connect's OAuth API with your existing authentication stack.
Standalone Connect allows applications with existing authentication systems to use AuthKit as their OAuth authorization server. You maintain your existing authentication stack while leveraging AuthKit’s OAuth infrastructure for token issuance and management.
Unlike standard AuthKit integration, Standalone Connect delegates authentication to your application, then handles the OAuth flow and token issuance for OAuth clients. This feature works with OAuth applications only – M2M applications use the client_credentials flow which doesn’t involve user authentication.
Use Standalone Connect when you:
Before testing your Standalone Connect integration, you’ll need to create an OAuth application in the WorkOS Dashboard. If you need to support MCP auth, you’ll need to enable Client ID Metadata Document (CIMD), which you can read more about in the MCP guide.
Navigate to Connect → Configuration in the WorkOS Dashboard and provide your Login URI.
The Login URI is where AuthKit will redirect users to authenticate with your existing system. You can only configure one Login URI per environment.
Your Login URI must:
external_auth_id query parameter.Example: https://your-app.example.com/auth/login
OAuth clients start the authorization flow with AuthKit following standard OAuth 2.0. This could be a third-party application, partner integration, or any OAuth-enabled client.
AuthKit redirects users to your Login URI with an external_auth_id parameter:
https://your-app.example.com/auth/login?external_auth_id=01J3X4Y5Z6A7B8C9D0E1F2G3H4
The external_auth_id is a temporary identifier used to complete the flow with AuthKit.
Your application authenticates users with your existing system. If users have an active session, skip to the next step.
AuthKit handles OAuth consent separately, so your application doesn’t need to display any consent screens.
Call the AuthKit completion API after authenticating the user, passing the external_auth_id that your Login URI originally received:
const response = await fetch('https://api.workos.com/authkit/oauth2/complete', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.WORKOS_API_KEY}`, }, body: JSON.stringify({ external_auth_id: externalAuthId, user: { id: user.id, email: user.email, first_name: user.firstName, last_name: user.lastName, metadata: { department: user.department }, }, }), }); const { redirect_uri } = await response.json();
Redirect users to the redirect_uri from the API response. AuthKit displays a consent screen if needed, issues tokens, and completes the OAuth flow.
You can provide user_consent_options to display options during the OAuth consent screen. This is useful when users need to choose specific resources or contexts (like a parent resource in your application) to grant access to.
Each consent option must include:
claim: The name of the access token claim that will hold the user’s selected value.type: The format of the option. Only enum is supported currently.label: Display text for the option.choices: Array of choices (can be flat or grouped).
const response = await fetch('https://api.workos.com/authkit/oauth2/complete', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.WORKOS_API_KEY}`, }, body: JSON.stringify({ external_auth_id, user: { id: user.id, email: user.email }, user_consent_options: [ { claim: 'urn:example:tenant', type: 'enum', label: 'Environment', choices: [ { group: 'Production', choices: [ { value: 'prod_us', label: 'US-East' }, { value: 'prod_eu', label: 'EU-West' }, ], }, { group: 'Development', choices: [ { value: 'dev_main', label: 'Development' }, { value: 'staging', label: 'Staging' }, ], }, ], }, ], }), });
The selected values appear as custom claims in the issued tokens. For example, if the user selects one of the above options, the token will include:
{ "sub": "user_123", "email": "user@example.com", "urn:example:tenant": "prod_us" // ... }
If the external_auth_id is invalid, the call to the AuthKit completion API will fail. Your application is free to choose how to handle this case, but redirecting to your application’s homepage is recommended.
Resource servers verify tokens issued by AuthKit using standard Connect token verification:
import { jwtVerify, createRemoteJWKSet } from 'jose'; const JWKS = createRemoteJWKSet(new URL('https://<subdomain>.authkit.app/oauth2/jwks')); async function verifyToken(token) { const { payload } = await jwtVerify(token, JWKS, { audience: 'client_123456789', issuer: 'https://<subdomain>.authkit.app', }); return payload; }
See the Connect documentation for details on token expiration, refresh tokens, and revocation.