Skip to content

Commit 5c5ed7c

Browse files
committed
create public API for methods that don't require an API key
1 parent 13903cd commit 5c5ed7c

File tree

10 files changed

+376
-193
lines changed

10 files changed

+376
-193
lines changed

package.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -103,14 +103,14 @@
103103
"require": "./lib/cjs/index.cjs",
104104
"default": "./lib/esm/index.js"
105105
},
106-
"./client": {
106+
"./public": {
107107
"types": {
108-
"require": "./lib/cjs/index.client.d.cts",
109-
"import": "./lib/esm/index.client.d.ts"
108+
"require": "./lib/cjs/index.public.d.cts",
109+
"import": "./lib/esm/index.public.d.ts"
110110
},
111-
"import": "./lib/esm/index.client.js",
112-
"require": "./lib/cjs/index.client.cjs",
113-
"default": "./lib/esm/index.client.js"
111+
"import": "./lib/esm/index.public.js",
112+
"require": "./lib/cjs/index.public.cjs",
113+
"default": "./lib/esm/index.public.js"
114114
},
115115
"./worker": {
116116
"types": {
Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { WorkOS } from './index.client';
1+
import { WorkOS } from './index.public';
22

3-
describe('WorkOS Client', () => {
4-
let workosClient: WorkOS;
3+
describe('WorkOS Public API', () => {
4+
let workosPublic: WorkOS;
55

66
beforeEach(() => {
7-
workosClient = new WorkOS({
7+
workosPublic = new WorkOS({
88
clientId: 'client_123',
99
apiHostname: 'api.workos.dev',
1010
});
@@ -15,7 +15,7 @@ describe('WorkOS Client', () => {
1515
expect(() => new WorkOS()).not.toThrow();
1616
});
1717

18-
it('should accept client configuration options', () => {
18+
it('should accept public configuration options', () => {
1919
const client = new WorkOS({
2020
clientId: 'test_client',
2121
apiHostname: 'test.api.com',
@@ -30,43 +30,43 @@ describe('WorkOS Client', () => {
3030

3131
describe('exposed services', () => {
3232
it('should expose webhooks service', () => {
33-
expect(workosClient.webhooks).toBeDefined();
34-
expect(workosClient.webhooks.verifyHeader).toBeDefined();
35-
expect(workosClient.webhooks.computeSignature).toBeDefined();
36-
expect(workosClient.webhooks.constructEvent).toBeDefined();
33+
expect(workosPublic.webhooks).toBeDefined();
34+
expect(workosPublic.webhooks.verifyHeader).toBeDefined();
35+
expect(workosPublic.webhooks.computeSignature).toBeDefined();
36+
expect(workosPublic.webhooks.constructEvent).toBeDefined();
3737
});
3838

3939
it('should expose actions service', () => {
40-
expect(workosClient.actions).toBeDefined();
41-
expect(workosClient.actions.verifyHeader).toBeDefined();
42-
expect(workosClient.actions.signResponse).toBeDefined();
43-
expect(workosClient.actions.constructAction).toBeDefined();
40+
expect(workosPublic.actions).toBeDefined();
41+
expect(workosPublic.actions.verifyHeader).toBeDefined();
42+
expect(workosPublic.actions.signResponse).toBeDefined();
43+
expect(workosPublic.actions.constructAction).toBeDefined();
4444
});
4545

4646
it('should expose client-safe user management methods', () => {
47-
expect(workosClient.userManagement).toBeDefined();
47+
expect(workosPublic.userManagement).toBeDefined();
4848
expect(
49-
workosClient.userManagement.authenticateWithCodeAndVerifier,
49+
workosPublic.userManagement.authenticateWithCodeAndVerifier,
5050
).toBeDefined();
51-
expect(workosClient.userManagement.getAuthorizationUrl).toBeDefined();
52-
expect(workosClient.userManagement.getLogoutUrl).toBeDefined();
53-
expect(workosClient.userManagement.getJwksUrl).toBeDefined();
51+
expect(workosPublic.userManagement.getAuthorizationUrl).toBeDefined();
52+
expect(workosPublic.userManagement.getLogoutUrl).toBeDefined();
53+
expect(workosPublic.userManagement.getJwksUrl).toBeDefined();
5454
});
5555

5656
it('should expose client-safe SSO methods', () => {
57-
expect(workosClient.sso).toBeDefined();
58-
expect(workosClient.sso.getAuthorizationUrl).toBeDefined();
57+
expect(workosPublic.sso).toBeDefined();
58+
expect(workosPublic.sso.getAuthorizationUrl).toBeDefined();
5959
});
6060

6161
it('should expose version', () => {
62-
expect(workosClient.version).toBeDefined();
63-
expect(typeof workosClient.version).toBe('string');
62+
expect(workosPublic.version).toBeDefined();
63+
expect(typeof workosPublic.version).toBe('string');
6464
});
6565
});
6666

6767
describe('method behavior', () => {
6868
it('should be able to call URL generation methods', () => {
69-
const authUrl = workosClient.userManagement.getAuthorizationUrl({
69+
const authUrl = workosPublic.userManagement.getAuthorizationUrl({
7070
clientId: 'client_123',
7171
redirectUri: 'https://example.com/callback',
7272
provider: 'GoogleOAuth',
@@ -78,12 +78,12 @@ describe('WorkOS Client', () => {
7878
});
7979

8080
it('should be able to call JWKS URL generation', () => {
81-
const jwksUrl = workosClient.userManagement.getJwksUrl('client_123');
81+
const jwksUrl = workosPublic.userManagement.getJwksUrl('client_123');
8282
expect(jwksUrl).toBe('https://api.workos.dev/sso/jwks/client_123');
8383
});
8484

8585
it('should be able to call logout URL generation', () => {
86-
const logoutUrl = workosClient.userManagement.getLogoutUrl({
86+
const logoutUrl = workosPublic.userManagement.getLogoutUrl({
8787
sessionId: 'session_123',
8888
returnTo: 'https://example.com',
8989
});
@@ -93,7 +93,7 @@ describe('WorkOS Client', () => {
9393
});
9494

9595
it('should be able to call SSO authorization URL generation', () => {
96-
const authUrl = workosClient.sso.getAuthorizationUrl({
96+
const authUrl = workosPublic.sso.getAuthorizationUrl({
9797
clientId: 'client_123',
9898
redirectUri: 'https://example.com/callback',
9999
provider: 'GoogleOAuth',
@@ -106,45 +106,45 @@ describe('WorkOS Client', () => {
106106

107107
describe('server-only methods should not be exposed', () => {
108108
it('should not expose getUser on userManagement', () => {
109-
expect((workosClient.userManagement as any).getUser).toBeUndefined();
109+
expect((workosPublic.userManagement as any).getUser).toBeUndefined();
110110
});
111111

112112
it('should not expose createUser on userManagement', () => {
113-
expect((workosClient.userManagement as any).createUser).toBeUndefined();
113+
expect((workosPublic.userManagement as any).createUser).toBeUndefined();
114114
});
115115

116116
it('should not expose listUsers on userManagement', () => {
117-
expect((workosClient.userManagement as any).listUsers).toBeUndefined();
117+
expect((workosPublic.userManagement as any).listUsers).toBeUndefined();
118118
});
119119

120120
it('should not expose updateUser on userManagement', () => {
121-
expect((workosClient.userManagement as any).updateUser).toBeUndefined();
121+
expect((workosPublic.userManagement as any).updateUser).toBeUndefined();
122122
});
123123

124124
it('should not expose server-only authentication methods', () => {
125125
expect(
126-
(workosClient.userManagement as any).authenticateWithPassword,
126+
(workosPublic.userManagement as any).authenticateWithPassword,
127127
).toBeUndefined();
128128
expect(
129-
(workosClient.userManagement as any).authenticateWithMagicAuth,
129+
(workosPublic.userManagement as any).authenticateWithMagicAuth,
130130
).toBeUndefined();
131131
expect(
132-
(workosClient.userManagement as any).authenticateWithRefreshToken,
132+
(workosPublic.userManagement as any).authenticateWithRefreshToken,
133133
).toBeUndefined();
134134
});
135135
});
136136

137137
describe('type safety', () => {
138138
it('should provide proper TypeScript types for exposed methods', () => {
139139
// These should compile without errors
140-
const authUrl: string = workosClient.userManagement.getAuthorizationUrl({
140+
const authUrl: string = workosPublic.userManagement.getAuthorizationUrl({
141141
clientId: 'test',
142142
redirectUri: 'https://example.com',
143143
provider: 'GoogleOAuth',
144144
});
145145

146146
const jwksUrl: string =
147-
workosClient.userManagement.getJwksUrl('client_id');
147+
workosPublic.userManagement.getJwksUrl('client_id');
148148

149149
expect(typeof authUrl).toBe('string');
150150
expect(typeof jwksUrl).toBe('string');
Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,30 @@
11
import { WorkOSBase } from './workos';
22
import { WorkOSOptions } from './common/interfaces';
33

4-
interface WorkOSClientOptions {
4+
// Export public methods directly (new approach)
5+
export * as userManagement from './public/user-management';
6+
export * as sso from './public/sso';
7+
8+
// Re-export types for convenience
9+
export type {
10+
AuthorizationURLOptions as UserManagementAuthorizationURLOptions,
11+
LogoutURLOptions,
12+
} from './public/user-management';
13+
14+
export type { SSOAuthorizationURLOptions } from './public/sso';
15+
16+
interface WorkOSPublicOptions {
517
clientId?: string;
618
apiHostname?: string;
719
https?: boolean;
820
port?: number;
921
}
1022

1123
/**
12-
* WorkOS client-safe SDK for browser environments.
24+
* WorkOS public-safe SDK for browser environments.
1325
* Only exposes methods that are safe to use without API keys.
26+
* @deprecated Use the direct method imports instead:
27+
* import { userManagement, sso } from '@workos-inc/node/public';
1428
*/
1529
export class WorkOS {
1630
private _base: WorkOSBase;
@@ -30,8 +44,8 @@ export class WorkOS {
3044
readonly webhooks: typeof WorkOSBase.prototype.webhooks;
3145
readonly actions: typeof WorkOSBase.prototype.actions;
3246

33-
constructor(options: WorkOSClientOptions = {}) {
34-
// Convert client options to base WorkOS options format
47+
constructor(options: WorkOSPublicOptions = {}) {
48+
// Convert public options to base WorkOS options format
3549
const baseOptions: WorkOSOptions = {
3650
clientId: options.clientId,
3751
apiHostname: options.apiHostname,
@@ -42,7 +56,7 @@ export class WorkOS {
4256
// Create base instance without API key
4357
this._base = new WorkOSBase(undefined, baseOptions);
4458

45-
// Initialize client-safe service methods
59+
// Initialize public-safe service methods
4660
this.userManagement = {
4761
authenticateWithCodeAndVerifier:
4862
this._base.userManagement.authenticateWithCodeAndVerifier.bind(
@@ -65,7 +79,7 @@ export class WorkOS {
6579
),
6680
};
6781

68-
// These services are fully client-safe - expose entirely
82+
// These services are fully public-safe - expose entirely
6983
this.webhooks = this._base.webhooks;
7084
this.actions = this._base.actions;
7185
}

src/public-exports.spec.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { userManagement, sso } from './index.public';
2+
3+
describe('New Public Exports', () => {
4+
describe('userManagement exports', () => {
5+
it('exports getAuthorizationUrl function', () => {
6+
expect(userManagement.getAuthorizationUrl).toBeDefined();
7+
expect(typeof userManagement.getAuthorizationUrl).toBe('function');
8+
});
9+
10+
it('exports getLogoutUrl function', () => {
11+
expect(userManagement.getLogoutUrl).toBeDefined();
12+
expect(typeof userManagement.getLogoutUrl).toBe('function');
13+
});
14+
15+
it('exports getJwksUrl function', () => {
16+
expect(userManagement.getJwksUrl).toBeDefined();
17+
expect(typeof userManagement.getJwksUrl).toBe('function');
18+
});
19+
20+
it('can generate authorization URL directly', () => {
21+
const url = userManagement.getAuthorizationUrl({
22+
clientId: 'client_123',
23+
redirectUri: 'https://app.com/callback',
24+
provider: 'authkit',
25+
});
26+
27+
expect(url).toContain('https://api.workos.com/user_management/authorize');
28+
expect(url).toContain('client_id=client_123');
29+
});
30+
});
31+
32+
describe('sso exports', () => {
33+
it('exports getAuthorizationUrl function', () => {
34+
expect(sso.getAuthorizationUrl).toBeDefined();
35+
expect(typeof sso.getAuthorizationUrl).toBe('function');
36+
});
37+
38+
it('can generate SSO authorization URL directly', () => {
39+
const url = sso.getAuthorizationUrl({
40+
clientId: 'client_123',
41+
redirectUri: 'https://app.com/callback',
42+
provider: 'GoogleOAuth',
43+
});
44+
45+
expect(url).toContain('https://api.workos.com/sso/authorize');
46+
expect(url).toContain('provider=GoogleOAuth');
47+
});
48+
});
49+
});

src/public/index.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* Public methods that can be safely used in without an API key.
3+
*/
4+
5+
// User Management public methods and types
6+
export * as userManagement from './user-management';
7+
8+
// SSO public methods and types
9+
export * as sso from './sso';
10+
11+
// Re-export specific types for convenience
12+
export type {
13+
AuthorizationURLOptions as UserManagementAuthorizationURLOptions,
14+
LogoutURLOptions,
15+
} from './user-management';
16+
17+
export type { SSOAuthorizationURLOptions } from './sso';

src/public/sso.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { toQueryString } from './utils';
2+
3+
export interface SSOAuthorizationURLOptions {
4+
clientId: string;
5+
connection?: string;
6+
organization?: string;
7+
domainHint?: string;
8+
loginHint?: string;
9+
provider?: string;
10+
providerQueryParams?: Record<string, string | boolean | number>;
11+
providerScopes?: string[];
12+
redirectUri: string;
13+
state?: string;
14+
}
15+
16+
/**
17+
* Generates the authorization URL for SSO authentication.
18+
* This method is safe to use in browser environments as it doesn't require an API key.
19+
*
20+
* @param options - SSO authorization URL options
21+
* @returns The authorization URL as a string
22+
* @throws Error if required arguments are missing
23+
*/
24+
export function getAuthorizationUrl(
25+
options: SSOAuthorizationURLOptions & { baseURL?: string },
26+
): string {
27+
const {
28+
connection,
29+
clientId,
30+
domainHint,
31+
loginHint,
32+
organization,
33+
provider,
34+
providerQueryParams,
35+
providerScopes,
36+
redirectUri,
37+
state,
38+
baseURL = 'https://api.workos.com',
39+
} = options;
40+
41+
if (!provider && !connection && !organization) {
42+
throw new Error(
43+
`Incomplete arguments. Need to specify either a 'connection', 'organization', or 'provider'.`,
44+
);
45+
}
46+
47+
const query = toQueryString({
48+
connection,
49+
organization,
50+
domain_hint: domainHint,
51+
login_hint: loginHint,
52+
provider,
53+
provider_query_params: providerQueryParams,
54+
provider_scopes: providerScopes,
55+
client_id: clientId,
56+
redirect_uri: redirectUri,
57+
response_type: 'code',
58+
state,
59+
});
60+
61+
return `${baseURL}/sso/authorize?${query}`;
62+
}

0 commit comments

Comments
 (0)