Stytch

Stytch

Build a marketplace with OAuth connected apps and secret rotation

Build a marketplace with OAuth connected apps and secret rotation

Jun 9, 20265 min readBy Stytch Examples

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

typescript
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:

json
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

typescript
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:

json
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

typescript
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:

json
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

typescript
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:

json
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

StytchStytch

Modern auth insights for high growth engineering teams

© 2026 Stytch. All rights reserved.