Enable other applications to access your user's identities.
WorkOS Connect is an upcoming feature. If you would like early access reach out to support@workos.com or via your team’s WorkOS Slack channel.
WorkOS Connect is a set of controls and APIs that developers can use to allow different types of applications to make use of your users’ identity and resources. Connect is built on top of industry-standard specifications like OAuth 2.0 and OpenID Connect in order to support many common use-cases out of the box.
Each Connect integration is defined as an Application, which can be created inside of the WorkOS Dashboard.
When creating an application, you choose the type of integration: OAuth or Machine-to-Machine (M2M). You also choose the level of trust: first-party or third-party.
Select OAuth when clients will be web-browsers or mobile-applications and the expected subject of the authentication is a User. Integrating with an OAuth application uses the underlying authorization_code
OAuth flow which is supported by many libraries and frameworks out of the box.
Upon successful authorization, the issued tokens will contain information about the user who signed in.
Select M2M when clients will be other machines, such as one of your customer’s applications. Integrating with an M2M application uses the underlying client_credentials
flow.
Unlike an OAuth application, the subject of the authorization is not an individual. Instead issued access tokens will contain an org_id
claim which represents the customer you are granting access to via the M2M application.
A common use-case for M2M applications is using its credentials as API access credentials for specific customers and partnerships.
Select first-party when the client is one that your team controls, such as supporting services that are deployed separately from your main application but still need access to your users’ identities. Examples include community forums or customer support portals.
Select third-party when the client is one of your customers or partners, but you do not directly control the integrating application. For this reason, you must also associate third-party applications with an Organization that represents the customer or partner.
A third-party OAuth application will generally have a “Sign in with [your application]” button on their login page, in the same way many sites have a “Sign in with Google” button, allowing you to offer similar functionality to your customers or partners. Unlike first-party applications, your users will be prompted in AuthKit to explicitly authorize the application before their identity is shared.
A third-party M2M application will generally be a service you are authorizing to access your application’s API using the access tokens obtained via the client_credentials
flow covered below.
Machine-to-machine applications can only be configured as third-party.
Once you’ve created an application, you can configure the following settings.
For OAuth applications, this is the final location users will be redirected to after successful authentication. Clients should use the Token Endpoint to exchange the code
for tokens at this location.
For third-party OAuth applications, the name and logo will be displayed to your users when they are prompted to authorize access. Both light and dark-mode logos are supported.
Applications can have up to 5 credentials. These are only shown once upon creation and do not expire. The application client_id
and client_secret
from a credential can be used to authenticate to the OAuth-based Connect APIs.
When sharing third-party app credentials with an external party, use a secure method – like encrypted email or file sharing – and make sure the recipient is properly authenticated.
When using Connect, there are two sides of the integration with each Application:
In the next sections we will cover the relevant APIs for each party.
After an external application has been issued credentials from a Connect Application, it can receive identity and/or access tokens depending on the type of Application.
Machine-to-machine applications can use the client_credentials
grant type with the Token Endpoint to obtain an access_token
to authenticate calls to your API.
const params = new URLSearchParams({ grant_type: 'client_credentials', client_id: process.env.M2M_APP_CLIENT_ID, client_secret: process.env.M2M_APP_CLIENT_SECRET, scope: 'openid profile email', }); const response = fetch('https://<subdomain>.authkit.app/oauth2/token', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: params, }).then((response) => response.json()); const accessToken = response.access_token;
Since machine-to-machine applications are associated with a particular organization, its issued access tokens contain an org_id
claim that your application’s API can use to control access.
OAuth applications use the OAuth 2.0 authorization_code
flow and also conform to the OpenID Connect (OIDC) spec, allowing external applications to receive tokens associated with a particular user.
Many OAuth and OIDC libraries support Connect applications out of the box needing only configuration:
import * as client from 'openid-client'; import { Strategy as OidcStrategy } from 'openid-client/passport'; import * as passport from 'passport'; const config = await client.discovery( 'https://<subdomain>.authkit.app', process.env.OAUTH_APP_CLIENT_ID, process.env.OAUTH_APP_CLIENT_SECRET, ); passport.use( new OidcStrategy( { config, scope: 'openid profile email offline_access', }, (_tokenset, _userinfo, _done) => { // Your code to persist tokenset and retrieve user info }, ), );
require "omniauth" use OmniAuth::Builder do provider :openid_connect, { name: :your_app, scope: %i[openid profile email offline_access], response_type: :code, uid_field: :id, discovery: true, issuer: "https://<subdomain>.authkit.app", client_options: { host: "<subdomain>.authkit.app", identifier: ENV["OAUTH_APP_CLIENT_ID"], secret: ENV["OAUTH_APP_CLIENT_SECRET"], redirect_uri: "http://your-app.example.com/auth/your_app/callback", } } end
Your application can verify the tokens sent by external applications for the purpose of authenticating requests using the JWKS for your environment. The process is similar to
import { jwtVerify, createRemoteJWKSet } from 'jose'; const JWKS = createRemoteJWKSet(new URL('https://<subdomain>.authkit.app/oauth2/jwks')); const bearerTokenMiddleware = async (req, res, next) => { const authHeader = req.headers.authorization; const token = authHeader?.match(/^Bearer (.+)$/)?.[1]; if (!token) { return res.status(401).json({ error: 'No token provided' }); } try { const { payload } = await jwtVerify(token, JWKS, { issuer: 'https://<subdomain>.authkit.app', audience: 'client_123456789', }); req.user = await findUserById(payload.sub); next(); } catch (err) { return res.status(401).json({ error: 'Invalid token' }); } }; // Example protected route app.get('/api/protected', bearerTokenMiddleware, (req, res) => { res.json({ data: 'Protected resource', user: req.user }); });
import { jwtVerify, createRemoteJWKSet } from 'jose'; const JWKS = createRemoteJWKSet(new URL('https://<subdomain>.authkit.app/oauth2/jwks')); const bearerTokenMiddleware = async (req, res, next) => { const authHeader = req.headers.authorization; const token = authHeader?.match(/^Bearer (.+)$/)?.[1]; if (!token) { return res.status(401).json({ error: 'No token provided' }); } try { const { payload } = await jwtVerify(token, JWKS, { issuer: 'https://<subdomain>.authkit.app', audience: 'client_123456789', }); req.organization = await findOrganizationById(payload.org_id); next(); } catch (err) { return res.status(401).json({ error: 'Invalid token' }); } }; // Example protected route app.get('/api/protected', bearerTokenMiddleware, (req, res) => { res.json({ data: 'Protected resource', organization: req.organization }); });
In addition to fast stateless verification, you can use the Token Introspection API to synchronously check whether a token is still valid.