@shadowob/oauth 0.4.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ ShadowOAuth: () => ShadowOAuth
24
+ });
25
+ module.exports = __toCommonJS(index_exports);
26
+
27
+ // src/client.ts
28
+ var DEFAULT_BASE_URL = "https://shadowob.com";
29
+ var ShadowOAuth = class {
30
+ baseUrl;
31
+ clientId;
32
+ clientSecret;
33
+ redirectUri;
34
+ constructor(config) {
35
+ this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
36
+ this.clientId = config.clientId;
37
+ this.clientSecret = config.clientSecret;
38
+ this.redirectUri = config.redirectUri;
39
+ }
40
+ /**
41
+ * Generate the authorization URL to redirect users to Shadow for login.
42
+ */
43
+ getAuthorizeUrl(options) {
44
+ const scope = options?.scope?.join(" ") ?? "user:read";
45
+ const params = new URLSearchParams({
46
+ response_type: "code",
47
+ client_id: this.clientId,
48
+ redirect_uri: this.redirectUri,
49
+ scope
50
+ });
51
+ if (options?.state) {
52
+ params.set("state", options.state);
53
+ }
54
+ return `${this.baseUrl}/oauth/authorize?${params.toString()}`;
55
+ }
56
+ /**
57
+ * Exchange an authorization code for access and refresh tokens.
58
+ */
59
+ async getToken(code) {
60
+ const res = await fetch(`${this.baseUrl}/api/oauth/token`, {
61
+ method: "POST",
62
+ headers: { "Content-Type": "application/json" },
63
+ body: JSON.stringify({
64
+ grant_type: "authorization_code",
65
+ code,
66
+ client_id: this.clientId,
67
+ client_secret: this.clientSecret,
68
+ redirect_uri: this.redirectUri
69
+ })
70
+ });
71
+ if (!res.ok) {
72
+ const body = await res.text().catch(() => "");
73
+ throw new Error(`Shadow OAuth token exchange failed (${res.status}): ${body}`);
74
+ }
75
+ const data = await res.json();
76
+ return {
77
+ accessToken: data.access_token,
78
+ refreshToken: data.refresh_token,
79
+ expiresIn: data.expires_in,
80
+ tokenType: data.token_type,
81
+ scope: data.scope
82
+ };
83
+ }
84
+ /**
85
+ * Refresh an access token using a refresh token.
86
+ */
87
+ async refreshToken(refreshToken) {
88
+ const res = await fetch(`${this.baseUrl}/api/oauth/token`, {
89
+ method: "POST",
90
+ headers: { "Content-Type": "application/json" },
91
+ body: JSON.stringify({
92
+ grant_type: "refresh_token",
93
+ refresh_token: refreshToken,
94
+ client_id: this.clientId,
95
+ client_secret: this.clientSecret
96
+ })
97
+ });
98
+ if (!res.ok) {
99
+ const body = await res.text().catch(() => "");
100
+ throw new Error(`Shadow OAuth token refresh failed (${res.status}): ${body}`);
101
+ }
102
+ const data = await res.json();
103
+ return {
104
+ accessToken: data.access_token,
105
+ refreshToken: data.refresh_token,
106
+ expiresIn: data.expires_in,
107
+ tokenType: data.token_type,
108
+ scope: data.scope
109
+ };
110
+ }
111
+ /**
112
+ * Get the authenticated user's information using an access token.
113
+ */
114
+ async getUser(accessToken) {
115
+ const res = await fetch(`${this.baseUrl}/api/oauth/userinfo`, {
116
+ headers: {
117
+ Authorization: `Bearer ${accessToken}`,
118
+ Accept: "application/json"
119
+ }
120
+ });
121
+ if (!res.ok) {
122
+ const body = await res.text().catch(() => "");
123
+ throw new Error(`Shadow OAuth userinfo failed (${res.status}): ${body}`);
124
+ }
125
+ return res.json();
126
+ }
127
+ };
128
+ // Annotate the CommonJS export names for ESM import in node:
129
+ 0 && (module.exports = {
130
+ ShadowOAuth
131
+ });
@@ -0,0 +1,78 @@
1
+ interface ShadowOAuthConfig {
2
+ /** Your app's client_id from Shadow Developer Portal */
3
+ clientId: string;
4
+ /** Your app's client_secret (keep server-side only) */
5
+ clientSecret: string;
6
+ /** The redirect URI registered with your app */
7
+ redirectUri: string;
8
+ /** Shadow API base URL (default: https://shadowob.com) */
9
+ baseUrl?: string;
10
+ }
11
+ interface ShadowOAuthTokens {
12
+ accessToken: string;
13
+ refreshToken: string;
14
+ expiresIn: number;
15
+ tokenType: string;
16
+ scope: string;
17
+ }
18
+ interface ShadowOAuthUser {
19
+ id: string;
20
+ username: string;
21
+ displayName: string | null;
22
+ avatarUrl: string | null;
23
+ email?: string;
24
+ }
25
+ type ShadowOAuthScope = 'user:read' | 'user:email';
26
+
27
+ /**
28
+ * Shadow OAuth SDK client.
29
+ *
30
+ * Use this in your server-side application to implement
31
+ * "Login with Shadow" via the OAuth 2.0 Authorization Code flow.
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * const oauth = new ShadowOAuth({
36
+ * clientId: 'shadow_xxx',
37
+ * clientSecret: 'shsec_xxx',
38
+ * redirectUri: 'https://myapp.com/callback',
39
+ * })
40
+ *
41
+ * // Step 1: Generate the authorization URL and redirect the user
42
+ * const url = oauth.getAuthorizeUrl({ scope: ['user:read', 'user:email'] })
43
+ *
44
+ * // Step 2: After callback, exchange the code for tokens
45
+ * const tokens = await oauth.getToken(code)
46
+ *
47
+ * // Step 3: Get user info
48
+ * const user = await oauth.getUser(tokens.accessToken)
49
+ * ```
50
+ */
51
+ declare class ShadowOAuth {
52
+ private baseUrl;
53
+ private clientId;
54
+ private clientSecret;
55
+ private redirectUri;
56
+ constructor(config: ShadowOAuthConfig);
57
+ /**
58
+ * Generate the authorization URL to redirect users to Shadow for login.
59
+ */
60
+ getAuthorizeUrl(options?: {
61
+ scope?: ShadowOAuthScope[];
62
+ state?: string;
63
+ }): string;
64
+ /**
65
+ * Exchange an authorization code for access and refresh tokens.
66
+ */
67
+ getToken(code: string): Promise<ShadowOAuthTokens>;
68
+ /**
69
+ * Refresh an access token using a refresh token.
70
+ */
71
+ refreshToken(refreshToken: string): Promise<ShadowOAuthTokens>;
72
+ /**
73
+ * Get the authenticated user's information using an access token.
74
+ */
75
+ getUser(accessToken: string): Promise<ShadowOAuthUser>;
76
+ }
77
+
78
+ export { ShadowOAuth, type ShadowOAuthConfig, type ShadowOAuthScope, type ShadowOAuthTokens, type ShadowOAuthUser };
@@ -0,0 +1,78 @@
1
+ interface ShadowOAuthConfig {
2
+ /** Your app's client_id from Shadow Developer Portal */
3
+ clientId: string;
4
+ /** Your app's client_secret (keep server-side only) */
5
+ clientSecret: string;
6
+ /** The redirect URI registered with your app */
7
+ redirectUri: string;
8
+ /** Shadow API base URL (default: https://shadowob.com) */
9
+ baseUrl?: string;
10
+ }
11
+ interface ShadowOAuthTokens {
12
+ accessToken: string;
13
+ refreshToken: string;
14
+ expiresIn: number;
15
+ tokenType: string;
16
+ scope: string;
17
+ }
18
+ interface ShadowOAuthUser {
19
+ id: string;
20
+ username: string;
21
+ displayName: string | null;
22
+ avatarUrl: string | null;
23
+ email?: string;
24
+ }
25
+ type ShadowOAuthScope = 'user:read' | 'user:email';
26
+
27
+ /**
28
+ * Shadow OAuth SDK client.
29
+ *
30
+ * Use this in your server-side application to implement
31
+ * "Login with Shadow" via the OAuth 2.0 Authorization Code flow.
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * const oauth = new ShadowOAuth({
36
+ * clientId: 'shadow_xxx',
37
+ * clientSecret: 'shsec_xxx',
38
+ * redirectUri: 'https://myapp.com/callback',
39
+ * })
40
+ *
41
+ * // Step 1: Generate the authorization URL and redirect the user
42
+ * const url = oauth.getAuthorizeUrl({ scope: ['user:read', 'user:email'] })
43
+ *
44
+ * // Step 2: After callback, exchange the code for tokens
45
+ * const tokens = await oauth.getToken(code)
46
+ *
47
+ * // Step 3: Get user info
48
+ * const user = await oauth.getUser(tokens.accessToken)
49
+ * ```
50
+ */
51
+ declare class ShadowOAuth {
52
+ private baseUrl;
53
+ private clientId;
54
+ private clientSecret;
55
+ private redirectUri;
56
+ constructor(config: ShadowOAuthConfig);
57
+ /**
58
+ * Generate the authorization URL to redirect users to Shadow for login.
59
+ */
60
+ getAuthorizeUrl(options?: {
61
+ scope?: ShadowOAuthScope[];
62
+ state?: string;
63
+ }): string;
64
+ /**
65
+ * Exchange an authorization code for access and refresh tokens.
66
+ */
67
+ getToken(code: string): Promise<ShadowOAuthTokens>;
68
+ /**
69
+ * Refresh an access token using a refresh token.
70
+ */
71
+ refreshToken(refreshToken: string): Promise<ShadowOAuthTokens>;
72
+ /**
73
+ * Get the authenticated user's information using an access token.
74
+ */
75
+ getUser(accessToken: string): Promise<ShadowOAuthUser>;
76
+ }
77
+
78
+ export { ShadowOAuth, type ShadowOAuthConfig, type ShadowOAuthScope, type ShadowOAuthTokens, type ShadowOAuthUser };
package/dist/index.js ADDED
@@ -0,0 +1,104 @@
1
+ // src/client.ts
2
+ var DEFAULT_BASE_URL = "https://shadowob.com";
3
+ var ShadowOAuth = class {
4
+ baseUrl;
5
+ clientId;
6
+ clientSecret;
7
+ redirectUri;
8
+ constructor(config) {
9
+ this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
10
+ this.clientId = config.clientId;
11
+ this.clientSecret = config.clientSecret;
12
+ this.redirectUri = config.redirectUri;
13
+ }
14
+ /**
15
+ * Generate the authorization URL to redirect users to Shadow for login.
16
+ */
17
+ getAuthorizeUrl(options) {
18
+ const scope = options?.scope?.join(" ") ?? "user:read";
19
+ const params = new URLSearchParams({
20
+ response_type: "code",
21
+ client_id: this.clientId,
22
+ redirect_uri: this.redirectUri,
23
+ scope
24
+ });
25
+ if (options?.state) {
26
+ params.set("state", options.state);
27
+ }
28
+ return `${this.baseUrl}/oauth/authorize?${params.toString()}`;
29
+ }
30
+ /**
31
+ * Exchange an authorization code for access and refresh tokens.
32
+ */
33
+ async getToken(code) {
34
+ const res = await fetch(`${this.baseUrl}/api/oauth/token`, {
35
+ method: "POST",
36
+ headers: { "Content-Type": "application/json" },
37
+ body: JSON.stringify({
38
+ grant_type: "authorization_code",
39
+ code,
40
+ client_id: this.clientId,
41
+ client_secret: this.clientSecret,
42
+ redirect_uri: this.redirectUri
43
+ })
44
+ });
45
+ if (!res.ok) {
46
+ const body = await res.text().catch(() => "");
47
+ throw new Error(`Shadow OAuth token exchange failed (${res.status}): ${body}`);
48
+ }
49
+ const data = await res.json();
50
+ return {
51
+ accessToken: data.access_token,
52
+ refreshToken: data.refresh_token,
53
+ expiresIn: data.expires_in,
54
+ tokenType: data.token_type,
55
+ scope: data.scope
56
+ };
57
+ }
58
+ /**
59
+ * Refresh an access token using a refresh token.
60
+ */
61
+ async refreshToken(refreshToken) {
62
+ const res = await fetch(`${this.baseUrl}/api/oauth/token`, {
63
+ method: "POST",
64
+ headers: { "Content-Type": "application/json" },
65
+ body: JSON.stringify({
66
+ grant_type: "refresh_token",
67
+ refresh_token: refreshToken,
68
+ client_id: this.clientId,
69
+ client_secret: this.clientSecret
70
+ })
71
+ });
72
+ if (!res.ok) {
73
+ const body = await res.text().catch(() => "");
74
+ throw new Error(`Shadow OAuth token refresh failed (${res.status}): ${body}`);
75
+ }
76
+ const data = await res.json();
77
+ return {
78
+ accessToken: data.access_token,
79
+ refreshToken: data.refresh_token,
80
+ expiresIn: data.expires_in,
81
+ tokenType: data.token_type,
82
+ scope: data.scope
83
+ };
84
+ }
85
+ /**
86
+ * Get the authenticated user's information using an access token.
87
+ */
88
+ async getUser(accessToken) {
89
+ const res = await fetch(`${this.baseUrl}/api/oauth/userinfo`, {
90
+ headers: {
91
+ Authorization: `Bearer ${accessToken}`,
92
+ Accept: "application/json"
93
+ }
94
+ });
95
+ if (!res.ok) {
96
+ const body = await res.text().catch(() => "");
97
+ throw new Error(`Shadow OAuth userinfo failed (${res.status}): ${body}`);
98
+ }
99
+ return res.json();
100
+ }
101
+ };
102
+ export {
103
+ ShadowOAuth
104
+ };
package/package.json CHANGED
@@ -1,15 +1,35 @@
1
1
  {
2
2
  "name": "@shadowob/oauth",
3
- "version": "0.4.0",
3
+ "version": "1.1.0",
4
4
  "description": "Shadow OAuth SDK — typed client for integrating Shadow OAuth login into third-party applications",
5
5
  "type": "module",
6
- "main": "./src/index.ts",
7
- "types": "./src/index.ts",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "require": "./dist/index.cjs",
9
+ "types": "./dist/index.d.ts",
8
10
  "exports": {
9
- ".": "./src/index.ts"
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "development": "./src/index.ts",
14
+ "import": "./dist/index.js",
15
+ "require": "./dist/index.cjs",
16
+ "default": "./dist/index.js"
17
+ }
10
18
  },
19
+ "files": [
20
+ "dist"
21
+ ],
11
22
  "dependencies": {},
23
+ "devDependencies": {
24
+ "tsup": "^8.5.0",
25
+ "typescript": "^5.9.3"
26
+ },
27
+ "publishConfig": {
28
+ "access": "public"
29
+ },
12
30
  "scripts": {
31
+ "build": "tsup",
32
+ "dev": "tsup --watch",
13
33
  "test": "vitest run",
14
34
  "test:watch": "vitest"
15
35
  }
package/src/client.ts DELETED
@@ -1,157 +0,0 @@
1
- import type {
2
- ShadowOAuthConfig,
3
- ShadowOAuthScope,
4
- ShadowOAuthTokens,
5
- ShadowOAuthUser,
6
- } from './types'
7
-
8
- const DEFAULT_BASE_URL = 'https://shadowob.com'
9
-
10
- /**
11
- * Shadow OAuth SDK client.
12
- *
13
- * Use this in your server-side application to implement
14
- * "Login with Shadow" via the OAuth 2.0 Authorization Code flow.
15
- *
16
- * @example
17
- * ```ts
18
- * const oauth = new ShadowOAuth({
19
- * clientId: 'shadow_xxx',
20
- * clientSecret: 'shsec_xxx',
21
- * redirectUri: 'https://myapp.com/callback',
22
- * })
23
- *
24
- * // Step 1: Generate the authorization URL and redirect the user
25
- * const url = oauth.getAuthorizeUrl({ scope: ['user:read', 'user:email'] })
26
- *
27
- * // Step 2: After callback, exchange the code for tokens
28
- * const tokens = await oauth.getToken(code)
29
- *
30
- * // Step 3: Get user info
31
- * const user = await oauth.getUser(tokens.accessToken)
32
- * ```
33
- */
34
- export class ShadowOAuth {
35
- private baseUrl: string
36
- private clientId: string
37
- private clientSecret: string
38
- private redirectUri: string
39
-
40
- constructor(config: ShadowOAuthConfig) {
41
- this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, '')
42
- this.clientId = config.clientId
43
- this.clientSecret = config.clientSecret
44
- this.redirectUri = config.redirectUri
45
- }
46
-
47
- /**
48
- * Generate the authorization URL to redirect users to Shadow for login.
49
- */
50
- getAuthorizeUrl(options?: { scope?: ShadowOAuthScope[]; state?: string }): string {
51
- const scope = options?.scope?.join(' ') ?? 'user:read'
52
- const params = new URLSearchParams({
53
- response_type: 'code',
54
- client_id: this.clientId,
55
- redirect_uri: this.redirectUri,
56
- scope,
57
- })
58
- if (options?.state) {
59
- params.set('state', options.state)
60
- }
61
- return `${this.baseUrl}/oauth/authorize?${params.toString()}`
62
- }
63
-
64
- /**
65
- * Exchange an authorization code for access and refresh tokens.
66
- */
67
- async getToken(code: string): Promise<ShadowOAuthTokens> {
68
- const res = await fetch(`${this.baseUrl}/api/oauth/token`, {
69
- method: 'POST',
70
- headers: { 'Content-Type': 'application/json' },
71
- body: JSON.stringify({
72
- grant_type: 'authorization_code',
73
- code,
74
- client_id: this.clientId,
75
- client_secret: this.clientSecret,
76
- redirect_uri: this.redirectUri,
77
- }),
78
- })
79
-
80
- if (!res.ok) {
81
- const body = await res.text().catch(() => '')
82
- throw new Error(`Shadow OAuth token exchange failed (${res.status}): ${body}`)
83
- }
84
-
85
- const data = (await res.json()) as {
86
- access_token: string
87
- refresh_token: string
88
- expires_in: number
89
- token_type: string
90
- scope: string
91
- }
92
-
93
- return {
94
- accessToken: data.access_token,
95
- refreshToken: data.refresh_token,
96
- expiresIn: data.expires_in,
97
- tokenType: data.token_type,
98
- scope: data.scope,
99
- }
100
- }
101
-
102
- /**
103
- * Refresh an access token using a refresh token.
104
- */
105
- async refreshToken(refreshToken: string): Promise<ShadowOAuthTokens> {
106
- const res = await fetch(`${this.baseUrl}/api/oauth/token`, {
107
- method: 'POST',
108
- headers: { 'Content-Type': 'application/json' },
109
- body: JSON.stringify({
110
- grant_type: 'refresh_token',
111
- refresh_token: refreshToken,
112
- client_id: this.clientId,
113
- client_secret: this.clientSecret,
114
- }),
115
- })
116
-
117
- if (!res.ok) {
118
- const body = await res.text().catch(() => '')
119
- throw new Error(`Shadow OAuth token refresh failed (${res.status}): ${body}`)
120
- }
121
-
122
- const data = (await res.json()) as {
123
- access_token: string
124
- refresh_token: string
125
- expires_in: number
126
- token_type: string
127
- scope: string
128
- }
129
-
130
- return {
131
- accessToken: data.access_token,
132
- refreshToken: data.refresh_token,
133
- expiresIn: data.expires_in,
134
- tokenType: data.token_type,
135
- scope: data.scope,
136
- }
137
- }
138
-
139
- /**
140
- * Get the authenticated user's information using an access token.
141
- */
142
- async getUser(accessToken: string): Promise<ShadowOAuthUser> {
143
- const res = await fetch(`${this.baseUrl}/api/oauth/userinfo`, {
144
- headers: {
145
- Authorization: `Bearer ${accessToken}`,
146
- Accept: 'application/json',
147
- },
148
- })
149
-
150
- if (!res.ok) {
151
- const body = await res.text().catch(() => '')
152
- throw new Error(`Shadow OAuth userinfo failed (${res.status}): ${body}`)
153
- }
154
-
155
- return res.json() as Promise<ShadowOAuthUser>
156
- }
157
- }
package/src/index.ts DELETED
@@ -1,7 +0,0 @@
1
- export { ShadowOAuth } from './client'
2
- export type {
3
- ShadowOAuthConfig,
4
- ShadowOAuthScope,
5
- ShadowOAuthTokens,
6
- ShadowOAuthUser,
7
- } from './types'
package/src/types.ts DELETED
@@ -1,28 +0,0 @@
1
- export interface ShadowOAuthConfig {
2
- /** Your app's client_id from Shadow Developer Portal */
3
- clientId: string
4
- /** Your app's client_secret (keep server-side only) */
5
- clientSecret: string
6
- /** The redirect URI registered with your app */
7
- redirectUri: string
8
- /** Shadow API base URL (default: https://shadowob.com) */
9
- baseUrl?: string
10
- }
11
-
12
- export interface ShadowOAuthTokens {
13
- accessToken: string
14
- refreshToken: string
15
- expiresIn: number
16
- tokenType: string
17
- scope: string
18
- }
19
-
20
- export interface ShadowOAuthUser {
21
- id: string
22
- username: string
23
- displayName: string | null
24
- avatarUrl: string | null
25
- email?: string
26
- }
27
-
28
- export type ShadowOAuthScope = 'user:read' | 'user:email'
package/tsconfig.json DELETED
@@ -1,8 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.json",
3
- "compilerOptions": {
4
- "rootDir": "./src",
5
- "outDir": "./dist"
6
- },
7
- "include": ["src"]
8
- }