Managing third-party OAuth integrations and rotating secrets securely in a B2B marketplace is complex and error-prone without proper controls. Stytch provides APIs for managing connected apps, OAuth flows, and automated secret rotation to simplify marketplace integrations. Use Stytch's connected apps endpoints to register OAuth providers, manage authorizations per organization, and enforce secure credential rotation. Handle multi-tenant OAuth flows safely by leveraging Stytch's B2B identity platform to isolate credentials and revoke access at the member level.
What this tutorial covers
- •Outcome: You can register OAuth apps, authorize users through secure flows, rotate secrets programmatically, and revoke access across your marketplace.
- •Endpoints used: `POST /v1/connected_apps/clients`, `POST /v1/b2b/idp/oauth/authorize/start`, `POST /v1/b2b/idp/oauth/authorize`, `GET /v1/b2b/organizations/{organization_id}/connected_apps`, `POST /v1/connected_apps/clients/{client_id}/secrets/rotate/start`, `POST /v1/b2b/organizations/{organization_id}/members/{member_id}/connected_apps/{connected_app_id}/revoke`
- •Language: typescript
- •Auth: API key (Authorization header with Bearer token)
- •Estimated implementation time: ~18 minutes
Step 1: Register an OAuth provider with Stytch connected apps
Create a connected app client in Stytch to register your OAuth provider. This endpoint stores the client credentials and configuration needed to initiate OAuth flows. Each connected app represents a third-party service your marketplace users can authorize.
Create a connected app client
1const stytch = require('stytch');
2
3const client = new stytch.B2BClient({
4 project_id: '${projectId}',
5 secret: '${secret}',
6});
7
8const params = {
9 client_type: "first_party",
10 client_name: "My OAuth Provider",
11 client_description: "OAuth provider for marketplace third-party integrations",
12 redirect_urls: ["https://example.com/callback"],
13 full_access_allowed: false,
14};
15
16const connectedApp = await client.connectedApp.clients.create(params);
17console.log(connectedApp);Response:
1const stytch = require('stytch');
2
3const client = new stytch.B2BClient({
4 project_id: '${projectId}',
5 secret: '${secret}',
6});
7
8const params = {
9 client_type: "first_party",
10 client_name: "My Sample Client",
11 client_description: "My sample client for testing out Connected Apps",
12 redirect_urls: ["https://example.com/callback"],
13 full_access_allowed: false,
14};
15
16const resp = await client.connectedApp.clients.create(params);
17
18console.log(resp);Step 2: Initiate OAuth authorization flow with Stytch
Start the OAuth authorization process using Stytch's B2B OAuth endpoint. This generates an authorization URL you redirect users to, ensuring the state parameter is securely bound to the user's session. Stytch handles state generation and validation to prevent CSRF attacks in multi-tenant marketplace environments.
Start OAuth authorization
Step 3: Complete OAuth authorization and exchange code for token
After the user authorizes, Stytch exchanges the authorization code for an access token and stores it securely within the organization member's connected app record. Submit the authorization code returned from the provider to complete the flow without exposing tokens to the frontend.
Submit authorization code
Step 4: Retrieve authorized connected apps for an organization
Query Stytch to list all connected apps authorized by members in an organization. This endpoint shows which integrations are active and when they were authorized. Use this to audit integrations, manage permissions, and display available connected apps in your marketplace UI.
List organization connected apps
1import stytch from 'stytch';
2
3const client = new stytch.B2BClient({
4 project_id: '${projectId}',
5 secret: '${secret}',
6});
7
8const params = {
9 organization_id: '${organizationId}',
10};
11
12const options = {
13 authorization: {
14 session_token: '${sessionToken}',
15 },
16};
17
18const resp = await client.organizations.connectedApps(params, options);
19console.log('Authorized connected apps:', resp);Response:
1const stytch = require('stytch');
2
3const client = new stytch.B2BClient({
4 project_id: '${projectId}',
5 secret: '${secret}',
6});
7
8const params = {
9 organization_id: "${organizationId}",
10};
11
12const options = {
13 authorization: {
14 session_token: '${sessionToken}',
15 },
16};
17
18client.organizations.connectedApps(params, options)
19 .then(resp => { console.log(resp) })
20 .catch(err => { console.log(err) });Step 5: Rotate connected app secrets with Stytch
Initiate secret rotation for a connected app client to generate a new credential and deprecate the old one. Stytch manages the rotation lifecycle securely. Regular secret rotation limits exposure window if a credential is compromised, following security best practices for long-lived marketplace credentials.
Start secret rotation
1const stytch = require('stytch');
2
3const client = new stytch.B2BClient({
4 project_id: '${projectId}',
5 secret: '${secret}',
6});
7
8const params = {
9 client_id: '${exampleM2MClientID}',
10};
11
12client.connectedApp.clients.secrets.rotateStart(params)
13 .then(resp => { console.log(resp) })
14 .catch(err => { console.log(err) });Response:
1const stytch = require('stytch');
2
3const client = new stytch.B2BClient({
4 project_id: '${projectId}',
5 secret: '${secret}',
6});
7
8const params = {
9 client_id: "${exampleM2MClientID}",
10};
11
12client.connectedApp.clients.secrets.rotateStart(params)
13 .then(resp => { console.log(resp) })
14 .catch(err => { console.log(err) });Step 6: Revoke connected app access for a team member
Remove a member's authorization for a connected app using Stytch's revoke endpoint. This immediately invalidates the stored access token and prevents further API calls. Use revocation to enforce least-privilege access when members leave teams, change roles, or when you need to disable an integration without touching other members' authorizations.
Revoke member app access
1const stytch = require('stytch');
2
3const client = new stytch.B2BClient({
4 project_id: '${projectId}',
5 secret: '${secret}',
6});
7
8const params = {
9 organization_id: '${organizationId}',
10 member_id: '${memberId}',
11 connected_app_id: '${exampleConnectedAppClientID}',
12};
13
14const options = {
15 authorization: {
16 session_token: '${sessionToken}',
17 },
18};
19
20client.organizations.members.connectedApps.revoke(params, options)
21 .then(resp => { console.log('Access revoked:', resp) })
22 .catch(err => { console.log('Revocation failed:', err) });Response:
1const stytch = require('stytch');
2
3const client = new stytch.B2BClient({
4 project_id: '${projectId}',
5 secret: '${secret}',
6});
7
8const params = {
9 organization_id: "${organizationId}",
10 member_id: "${memberId}",
11 connected_app_id: "${exampleConnectedAppClientID}",
12};
13
14const options = {
15 authorization: {
16 session_token: '${sessionToken}',
17 },
18};
19
20client.organizations.members.connectedApps.revoke(params, options)
21 .then(resp => { console.log(resp) })
22 .catch(err => { console.log(err) });Common pitfalls when using Stytch
- •State parameter must survive redirect round-trip. Stytch generates a state value at authorization start; you must store it server-side tied to the user's session. On callback, validate the returned state matches your stored value before exchanging the authorization code. Mismatch indicates a CSRF attempt.
- •Redirect URI must exactly match registered configuration. OAuth providers like Atlassian enforce exact string matching of the redirect_uri parameter against the Callback URL configured in their developer console. A trailing slash, protocol mismatch, or query parameter difference will cause the authorization to fail silently.
- •Secret rotation grace period requires coordination. When Stytch rotates a secret, both old and new credentials work during the grace period to allow clients time to update. After the grace period expires, only the new secret is valid. Monitor your integration logs during rotation to ensure consumers are not still using the deprecated secret.
- •Member-level revocation does not affect organization configs. Revoking a connected app for one member only removes that member's access token. The connected app client remains registered in Stytch, and other organization members can still authorize it. Manage per-member and per-organization policies separately.
Ready to ship secure marketplace integrations? Get started with Stytch and handle OAuth flows, secret rotation, and revocation with confidence.
Documentation references
The code examples in this tutorial are grounded in the following docs pages:
- •
- •
- •
- •
- •
- •
Build modern authentication faster with Stytch
Join leading teams using Stytch APIs to ship secure auth flows, reduce friction, and strengthen your product’s security.
Read More Blog Posts
Onboard clinic organizations and member access with Stytch B2B Discovery
Healthcare SaaS platforms need to rapidly onboard clinic organizations and provision member access without manual admin overhead or complex signup flows.
Stytch vs WorkOS: Building Enterprise Agent Workflows with Modern Auth
Engineering teams at B2B SaaS companies must choose between Stytch and WorkOS to build secure, scalable authentication for agent-driven workflows that demand fi

