Skip to content

Commit f800b4f

Browse files
authored
fix(backend): Add samlConnection and oauthConfig into EnterpriseConnection (#8326)
1 parent 2d4e792 commit f800b4f

5 files changed

Lines changed: 205 additions & 26 deletions

File tree

.changeset/fair-doodles-change.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@clerk/backend': patch
3+
---
4+
5+
Introduce `samlConnection` and `oauthConfig` into the `EnterpriseConnection` resource.

packages/backend/src/api/__tests__/EnterpriseConnectionApi.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,30 @@ describe('EnterpriseConnectionAPI', () => {
2222
sync_user_attributes: false,
2323
allow_subdomains: false,
2424
disable_additional_identifications: false,
25+
saml_connection: {
26+
id: 'samlc_1',
27+
name: 'Acme SAML',
28+
idp_entity_id: 'https://idp.example.com',
29+
idp_sso_url: 'https://idp.example.com/sso',
30+
idp_certificate: '-----BEGIN CERTIFICATE-----',
31+
idp_metadata_url: 'https://idp.example.com/metadata',
32+
idp_metadata: '<xml/>',
33+
acs_url: 'https://clerk.example.com/v1/saml/acs',
34+
sp_entity_id: 'https://clerk.example.com',
35+
sp_metadata_url: 'https://clerk.example.com/v1/saml/metadata',
36+
sync_user_attributes: true,
37+
allow_subdomains: true,
38+
allow_idp_initiated: false,
39+
},
40+
oauth_config: {
41+
id: 'eaoc_1',
42+
name: 'Acme OIDC',
43+
client_id: 'client_abc',
44+
discovery_url: 'https://oauth.example.com/.well-known/openid-configuration',
45+
logo_public_url: 'https://img.example.com/logo.png',
46+
created_at: 1672531200000,
47+
updated_at: 1672531200000,
48+
},
2549
};
2650

2751
describe('createEnterpriseConnection', () => {
@@ -178,6 +202,12 @@ describe('EnterpriseConnectionAPI', () => {
178202
expect(response.domains).toEqual(['clerk.dev']);
179203
expect(response.active).toBe(true);
180204
expect(response.organizationId).toBeNull();
205+
expect(response.samlConnection).not.toBeNull();
206+
expect(response.samlConnection?.id).toBe('samlc_1');
207+
expect(response.samlConnection?.idpEntityId).toBe('https://idp.example.com');
208+
expect(response.oauthConfig).not.toBeNull();
209+
expect(response.oauthConfig?.clientId).toBe('client_abc');
210+
expect(response.oauthConfig?.discoveryUrl).toBe('https://oauth.example.com/.well-known/openid-configuration');
181211
});
182212
});
183213

packages/backend/src/api/resources/EnterpriseConnection.ts

Lines changed: 138 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,131 @@
1-
import type { EnterpriseConnectionJSON } from './JSON';
1+
import type {
2+
EnterpriseConnectionJSON,
3+
EnterpriseConnectionOauthConfigJSON,
4+
EnterpriseConnectionSamlConnectionJSON,
5+
} from './JSON';
6+
7+
export class EnterpriseConnectionSamlConnection {
8+
constructor(
9+
/**
10+
* The unique identifier for the SAML connection.
11+
*/
12+
readonly id: string,
13+
/**
14+
* The name to use as a label for the connection.
15+
*/
16+
readonly name: string,
17+
/**
18+
* The Entity ID as provided by the Identity Provider (IdP).
19+
*/
20+
readonly idpEntityId: string,
21+
/**
22+
* The Single-Sign On URL as provided by the Identity Provider (IdP).
23+
*/
24+
readonly idpSsoUrl: string,
25+
/**
26+
* The X.509 certificate as provided by the Identity Provider (IdP).
27+
*/
28+
readonly idpCertificate: string,
29+
/**
30+
* The URL which serves the Identity Provider (IdP) metadata.
31+
*/
32+
readonly idpMetadataUrl: string,
33+
/**
34+
* The XML content of the Identity Provider (IdP) metadata file.
35+
*/
36+
readonly idpMetadata: string,
37+
/**
38+
* The Assertion Consumer Service (ACS) URL of the connection.
39+
*/
40+
readonly acsUrl: string,
41+
/**
42+
* The Entity ID as provided by the Service Provider (Clerk).
43+
*/
44+
readonly spEntityId: string,
45+
/**
46+
* The metadata URL as provided by the Service Provider (Clerk).
47+
*/
48+
readonly spMetadataUrl: string,
49+
/**
50+
* Indicates whether the connection syncs user attributes between the IdP and Clerk.
51+
*/
52+
readonly syncUserAttributes: boolean,
53+
/**
54+
* Indicates whether users with an email address subdomain are allowed to use this connection.
55+
*/
56+
readonly allowSubdomains: boolean,
57+
/**
58+
* Indicates whether Identity Provider (IdP) initiated flows are allowed.
59+
*/
60+
readonly allowIdpInitiated: boolean,
61+
) {}
62+
63+
static fromJSON(data: EnterpriseConnectionSamlConnectionJSON): EnterpriseConnectionSamlConnection {
64+
return new EnterpriseConnectionSamlConnection(
65+
data.id,
66+
data.name,
67+
data.idp_entity_id,
68+
data.idp_sso_url,
69+
data.idp_certificate,
70+
data.idp_metadata_url,
71+
data.idp_metadata,
72+
data.acs_url,
73+
data.sp_entity_id,
74+
data.sp_metadata_url,
75+
data.sync_user_attributes,
76+
data.allow_subdomains,
77+
data.allow_idp_initiated,
78+
);
79+
}
80+
}
81+
82+
/**
83+
* OAuth configuration included on a Backend API {@link EnterpriseConnection} response.
84+
*/
85+
export class EnterpriseConnectionOauthConfig {
86+
constructor(
87+
/**
88+
* The unique identifier for the OAuth configuration.
89+
*/
90+
readonly id: string,
91+
/**
92+
* The name to use as a label for the configuration.
93+
*/
94+
readonly name: string,
95+
/**
96+
* The OAuth client ID.
97+
*/
98+
readonly clientId: string,
99+
/**
100+
* The OpenID Connect discovery URL.
101+
*/
102+
readonly discoveryUrl: string,
103+
/**
104+
* The public URL of the OAuth provider logo, if available.
105+
*/
106+
readonly logoPublicUrl: string,
107+
/**
108+
* The date when the configuration was first created.
109+
*/
110+
readonly createdAt: number,
111+
/**
112+
* The date when the configuration was last updated.
113+
*/
114+
readonly updatedAt: number,
115+
) {}
116+
117+
static fromJSON(data: EnterpriseConnectionOauthConfigJSON): EnterpriseConnectionOauthConfig {
118+
return new EnterpriseConnectionOauthConfig(
119+
data.id,
120+
data.name,
121+
data.client_id,
122+
data.discovery_url,
123+
data.logo_public_url,
124+
data.created_at,
125+
data.updated_at,
126+
);
127+
}
128+
}
2129

3130
/**
4131
* The Backend `EnterpriseConnection` object holds information about an enterprise connection (SAML or OAuth) for an instance or organization.
@@ -45,6 +172,14 @@ export class EnterpriseConnection {
45172
* The date when the connection was last updated.
46173
*/
47174
readonly updatedAt: number,
175+
/**
176+
* SAML connection details when the enterprise connection uses SAML.
177+
*/
178+
readonly samlConnection: EnterpriseConnectionSamlConnection | null,
179+
/**
180+
* OAuth (OIDC) configuration when the enterprise connection uses OAuth.
181+
*/
182+
readonly oauthConfig: EnterpriseConnectionOauthConfig | null,
48183
) {}
49184

50185
static fromJSON(data: EnterpriseConnectionJSON): EnterpriseConnection {
@@ -59,6 +194,8 @@ export class EnterpriseConnection {
59194
data.disable_additional_identifications,
60195
data.created_at,
61196
data.updated_at,
197+
data.saml_connection != null ? EnterpriseConnectionSamlConnection.fromJSON(data.saml_connection) : null,
198+
data.oauth_config != null ? EnterpriseConnectionOauthConfig.fromJSON(data.oauth_config) : null,
62199
);
63200
}
64201
}

packages/backend/src/api/resources/JSON.ts

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -704,6 +704,32 @@ export interface PaginatedResponseJSON {
704704
total_count?: number;
705705
}
706706

707+
export interface EnterpriseConnectionSamlConnectionJSON {
708+
id: string;
709+
name: string;
710+
idp_entity_id: string;
711+
idp_sso_url: string;
712+
idp_certificate: string;
713+
idp_metadata_url: string;
714+
idp_metadata: string;
715+
acs_url: string;
716+
sp_entity_id: string;
717+
sp_metadata_url: string;
718+
sync_user_attributes: boolean;
719+
allow_subdomains: boolean;
720+
allow_idp_initiated: boolean;
721+
}
722+
723+
export interface EnterpriseConnectionOauthConfigJSON {
724+
id: string;
725+
name: string;
726+
client_id: string;
727+
discovery_url: string;
728+
logo_public_url: string;
729+
created_at: number;
730+
updated_at: number;
731+
}
732+
707733
export interface EnterpriseConnectionJSON extends ClerkResourceJSON {
708734
object: typeof ObjectType.EnterpriseConnection;
709735
name: string;
@@ -715,31 +741,8 @@ export interface EnterpriseConnectionJSON extends ClerkResourceJSON {
715741
disable_additional_identifications: boolean;
716742
created_at: number;
717743
updated_at: number;
718-
saml_connection?: Pick<
719-
SamlConnectionJSON,
720-
| 'id'
721-
| 'name'
722-
| 'idp_entity_id'
723-
| 'idp_sso_url'
724-
| 'idp_certificate'
725-
| 'idp_metadata_url'
726-
| 'idp_metadata'
727-
| 'acs_url'
728-
| 'sp_entity_id'
729-
| 'sp_metadata_url'
730-
| 'sync_user_attributes'
731-
| 'allow_subdomains'
732-
| 'allow_idp_initiated'
733-
>;
734-
oauth_config?: {
735-
id: string;
736-
name: string;
737-
client_id: string;
738-
discovery_url: string;
739-
logo_public_url: string;
740-
created_at: number;
741-
updated_at: number;
742-
};
744+
saml_connection?: EnterpriseConnectionSamlConnectionJSON | null;
745+
oauth_config?: EnterpriseConnectionOauthConfigJSON | null;
743746
}
744747

745748
export interface SamlConnectionJSON extends ClerkResourceJSON {

packages/backend/src/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ export type {
6767
EmailJSON,
6868
EmailAddressJSON,
6969
EnterpriseConnectionJSON,
70+
EnterpriseConnectionOauthConfigJSON,
71+
EnterpriseConnectionSamlConnectionJSON,
7072
ExternalAccountJSON,
7173
IdentificationLinkJSON,
7274
InstanceJSON,
@@ -123,6 +125,8 @@ export type {
123125
Domain,
124126
EmailAddress,
125127
EnterpriseConnection,
128+
EnterpriseConnectionOauthConfig,
129+
EnterpriseConnectionSamlConnection,
126130
ExternalAccount,
127131
Feature,
128132
Instance,

0 commit comments

Comments
 (0)