@devoven/oauth 0.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.
Files changed (53) hide show
  1. package/README.md +281 -0
  2. package/dist/application/ports/get-auth-url.port.d.ts +3 -0
  3. package/dist/application/ports/get-auth-url.port.js +3 -0
  4. package/dist/application/ports/get-auth-url.port.js.map +1 -0
  5. package/dist/application/ports/handle-callback.port.d.ts +4 -0
  6. package/dist/application/ports/handle-callback.port.js +3 -0
  7. package/dist/application/ports/handle-callback.port.js.map +1 -0
  8. package/dist/application/ports/oauth-provider.port.d.ts +15 -0
  9. package/dist/application/ports/oauth-provider.port.js +3 -0
  10. package/dist/application/ports/oauth-provider.port.js.map +1 -0
  11. package/dist/application/ports/oauth-state-store.port.d.ts +4 -0
  12. package/dist/application/ports/oauth-state-store.port.js +3 -0
  13. package/dist/application/ports/oauth-state-store.port.js.map +1 -0
  14. package/dist/application/use-cases/get-auth-url.use-case.d.ts +9 -0
  15. package/dist/application/use-cases/get-auth-url.use-case.js +38 -0
  16. package/dist/application/use-cases/get-auth-url.use-case.js.map +1 -0
  17. package/dist/application/use-cases/handle-callback.use-case.d.ts +10 -0
  18. package/dist/application/use-cases/handle-callback.use-case.js +42 -0
  19. package/dist/application/use-cases/handle-callback.use-case.js.map +1 -0
  20. package/dist/domain/entities/oauth-profile.entity.d.ts +16 -0
  21. package/dist/domain/entities/oauth-profile.entity.js +26 -0
  22. package/dist/domain/entities/oauth-profile.entity.js.map +1 -0
  23. package/dist/domain/value-objects/oauth-token.vo.d.ts +15 -0
  24. package/dist/domain/value-objects/oauth-token.vo.js +26 -0
  25. package/dist/domain/value-objects/oauth-token.vo.js.map +1 -0
  26. package/dist/index.d.ts +13 -0
  27. package/dist/index.js +20 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/infrastructure/providers/google-oauth.provider.d.ts +15 -0
  30. package/dist/infrastructure/providers/google-oauth.provider.js +111 -0
  31. package/dist/infrastructure/providers/google-oauth.provider.js.map +1 -0
  32. package/dist/infrastructure/providers/line-oauth.provider.d.ts +16 -0
  33. package/dist/infrastructure/providers/line-oauth.provider.js +118 -0
  34. package/dist/infrastructure/providers/line-oauth.provider.js.map +1 -0
  35. package/dist/infrastructure/state/in-memory-state-store.d.ts +8 -0
  36. package/dist/infrastructure/state/in-memory-state-store.js +49 -0
  37. package/dist/infrastructure/state/in-memory-state-store.js.map +1 -0
  38. package/dist/oauth.module-definition.d.ts +18 -0
  39. package/dist/oauth.module-definition.js +9 -0
  40. package/dist/oauth.module-definition.js.map +1 -0
  41. package/dist/oauth.module.d.ts +8 -0
  42. package/dist/oauth.module.js +102 -0
  43. package/dist/oauth.module.js.map +1 -0
  44. package/dist/presentation/controllers/oauth.controller.d.ts +17 -0
  45. package/dist/presentation/controllers/oauth.controller.js +73 -0
  46. package/dist/presentation/controllers/oauth.controller.js.map +1 -0
  47. package/dist/presentation/dto/oauth-callback-query.dto.d.ts +4 -0
  48. package/dist/presentation/dto/oauth-callback-query.dto.js +27 -0
  49. package/dist/presentation/dto/oauth-callback-query.dto.js.map +1 -0
  50. package/dist/presentation/dto/oauth-profile-response.dto.d.ts +9 -0
  51. package/dist/presentation/dto/oauth-profile-response.dto.js +16 -0
  52. package/dist/presentation/dto/oauth-profile-response.dto.js.map +1 -0
  53. package/package.json +50 -0
package/README.md ADDED
@@ -0,0 +1,281 @@
1
+ # @devoven/oauth
2
+
3
+ OAuth 2.0 / OIDC module for multi-provider authentication in NestJS. Wire up Google, LINE, or any custom provider with a single import.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @devoven/oauth
9
+ # or
10
+ pnpm add @devoven/oauth
11
+ ```
12
+
13
+ ## Peer Dependencies
14
+
15
+ Install the standard NestJS validation stack if your app does not already have it:
16
+
17
+ ```bash
18
+ npm install class-validator class-transformer
19
+ ```
20
+
21
+ `@nestjs/common`, `@nestjs/core`, `rxjs`, and `reflect-metadata` are expected to already be present in any NestJS application.
22
+
23
+ `google-auth-library` is bundled with this package and does not need to be installed separately.
24
+
25
+ ## Quick Start
26
+
27
+ ```typescript
28
+ import { OAuthModule, GoogleOAuthProvider } from '@devoven/oauth';
29
+
30
+ @Module({
31
+ imports: [
32
+ OAuthModule.register({
33
+ providers: [
34
+ {
35
+ name: 'google',
36
+ provider: GoogleOAuthProvider,
37
+ clientId: process.env.GOOGLE_CLIENT_ID,
38
+ clientSecret: process.env.GOOGLE_CLIENT_SECRET,
39
+ callbackUrl: 'https://example.com/oauth/google/callback',
40
+ },
41
+ ],
42
+ }),
43
+ ],
44
+ })
45
+ export class AppModule {}
46
+ ```
47
+
48
+ This registers the `OAuthController` at `/oauth`, giving you:
49
+
50
+ - `GET /oauth/google` — redirects the user to Google's consent screen
51
+ - `GET /oauth/google/callback` — handles the code exchange and returns the resolved profile
52
+
53
+ ## Module Options
54
+
55
+ | Option | Type | Default | Description |
56
+ |--------|------|---------|-------------|
57
+ | `providers` | `Array<OAuthProviderOption \| OAuthProviderPort>` | — (required) | Provider configs or pre-constructed provider instances |
58
+ | `stateStore` | Class or instance of `OAuthStateStorePort` | `InMemoryStateStore` | CSRF state store. Default TTL is 5 minutes |
59
+ | `controller` | `boolean` | `true` | Mount `OAuthController`. Set to `false` to handle routes yourself |
60
+ | `callbackSuccessUrl` | `string` | `undefined` | When set, the callback endpoint redirects to this URL with profile fields as query parameters instead of returning JSON |
61
+
62
+ ### OAuthProviderOption
63
+
64
+ | Field | Type | Description |
65
+ |-------|------|-------------|
66
+ | `name` | `string` | Key used in the URL path (e.g. `'google'` → `/oauth/google`) |
67
+ | `provider` | Constructor | A class implementing `OAuthProviderPort` (e.g. `GoogleOAuthProvider`) |
68
+ | `clientId` | `string` | OAuth app client ID |
69
+ | `clientSecret` | `string` | OAuth app client secret |
70
+ | `callbackUrl` | `string` | Absolute URL registered with the provider |
71
+ | `scopes` | `string[]` | Optional scope list. Defaults to the provider's built-in defaults |
72
+
73
+ Alternatively, pass a pre-constructed `OAuthProviderPort` instance directly in the `providers` array.
74
+
75
+ ## Async Registration
76
+
77
+ ```typescript
78
+ import { OAuthModule, GoogleOAuthProvider, LineOAuthProvider } from '@devoven/oauth';
79
+ import { ConfigService } from '@nestjs/config';
80
+
81
+ @Module({
82
+ imports: [
83
+ OAuthModule.registerAsync({
84
+ useFactory: (config: ConfigService) => ({
85
+ callbackSuccessUrl: config.get('OAUTH_SUCCESS_URL'),
86
+ providers: [
87
+ {
88
+ name: 'google',
89
+ provider: GoogleOAuthProvider,
90
+ clientId: config.get('GOOGLE_CLIENT_ID'),
91
+ clientSecret: config.get('GOOGLE_CLIENT_SECRET'),
92
+ callbackUrl: config.get('GOOGLE_CALLBACK_URL'),
93
+ },
94
+ {
95
+ name: 'line',
96
+ provider: LineOAuthProvider,
97
+ clientId: config.get('LINE_CLIENT_ID'),
98
+ clientSecret: config.get('LINE_CLIENT_SECRET'),
99
+ callbackUrl: config.get('LINE_CALLBACK_URL'),
100
+ },
101
+ ],
102
+ }),
103
+ inject: [ConfigService],
104
+ }),
105
+ ],
106
+ })
107
+ export class AppModule {}
108
+ ```
109
+
110
+ ## REST API
111
+
112
+ All endpoints are served under the `/oauth` prefix.
113
+
114
+ ### Redirect to provider
115
+
116
+ ```
117
+ GET /oauth/:provider
118
+ ```
119
+
120
+ Generates a CSRF state token, stores it, and issues a `302` redirect to the provider's authorization URL.
121
+
122
+ | Parameter | Where | Description |
123
+ |-----------|-------|-------------|
124
+ | `provider` | path | Provider name as registered in `providers` (e.g. `google`, `line`) |
125
+
126
+ **Response:** `302 Found` — Location header points to the provider's authorization URL.
127
+
128
+ ### Handle callback
129
+
130
+ ```
131
+ GET /oauth/:provider/callback?code=...&state=...
132
+ ```
133
+
134
+ Verifies the state, exchanges the authorization code for tokens, fetches the user profile, and returns it.
135
+
136
+ | Parameter | Where | Description |
137
+ |-----------|-------|-------------|
138
+ | `provider` | path | Provider name |
139
+ | `code` | query | Authorization code from the provider |
140
+ | `state` | query | CSRF state value returned by the provider |
141
+
142
+ **Response without `callbackSuccessUrl`:** `200 OK`
143
+
144
+ ```json
145
+ {
146
+ "provider": "google",
147
+ "providerId": "1234567890",
148
+ "email": "user@example.com",
149
+ "name": "Jane Doe",
150
+ "avatarUrl": "https://example.com/avatar.jpg"
151
+ }
152
+ ```
153
+
154
+ Fields `email`, `name`, and `avatarUrl` may be `null` depending on the scopes granted and the provider's response.
155
+
156
+ **Response with `callbackSuccessUrl`:** `302 Found` — redirects to `callbackSuccessUrl` with the profile fields appended as query parameters (`provider`, `providerId`, `email`, `name`, `avatarUrl`).
157
+
158
+ ## Built-in Providers
159
+
160
+ ### GoogleOAuthProvider
161
+
162
+ Default scopes: `openid profile email`
163
+
164
+ Verifies the `id_token` using the Google auth library when present; falls back to the UserInfo endpoint otherwise.
165
+
166
+ ### LineOAuthProvider
167
+
168
+ Default scopes: `openid profile`
169
+
170
+ Verifies the `id_token` via LINE's `/oauth2/v2.1/verify` endpoint when present; falls back to the UserInfo endpoint otherwise.
171
+
172
+ Both classes accept a custom `scopes` array to override the defaults.
173
+
174
+ ## Architecture
175
+
176
+ ### Port / Token Mapping
177
+
178
+ | DI Token | Interface | Default Implementation | Purpose |
179
+ |----------|-----------|------------------------|---------|
180
+ | `'GetAuthorizationUrlPort'` | `GetAuthorizationUrlPort` | `GetAuthorizationUrlUseCase` | Generate a provider authorization URL with a fresh state token |
181
+ | `'HandleCallbackPort'` | `HandleCallbackPort` | `HandleCallbackUseCase` | Verify state, exchange code, return `OAuthProfile` |
182
+ | `'OAuthProviderRegistry'` | `OAuthProviderRegistry` (`Map<string, OAuthProviderPort>`) | Built from `providers` option | Registry of provider name → provider instance |
183
+ | `'OAuthStateStorePort'` | `OAuthStateStorePort` | `InMemoryStateStore` | Generate and verify CSRF state tokens |
184
+
185
+ `GetAuthorizationUrlPort` and `HandleCallbackPort` are exported from the module.
186
+
187
+ ## Domain Model
188
+
189
+ ### OAuthProfile
190
+
191
+ | Property | Type | Description |
192
+ |----------|------|-------------|
193
+ | `provider` | `string` | Provider name (e.g. `'google'`) |
194
+ | `providerId` | `string` | Provider-scoped user ID |
195
+ | `email` | `string \| null` | User email, if available |
196
+ | `name` | `string \| null` | Display name, if available |
197
+ | `avatarUrl` | `string \| null` | Profile picture URL, if available |
198
+ | `id` | `string` | Composite key: `provider:providerId` |
199
+
200
+ ### OAuthToken
201
+
202
+ | Property | Type | Description |
203
+ |----------|------|-------------|
204
+ | `accessToken` | `string` | Bearer token for API calls |
205
+ | `refreshToken` | `string \| null` | Refresh token, if issued |
206
+ | `expiresAt` | `Date \| null` | Computed from `expiresIn` when not provided directly |
207
+ | `idToken` | `string \| null` | OIDC ID token, if present |
208
+ | `isExpired` | `boolean` | `true` when `expiresAt` is in the past |
209
+
210
+ ## Custom Adapters
211
+
212
+ ### Custom state store
213
+
214
+ Provide a class or instance that implements `OAuthStateStorePort` for persistent CSRF state (e.g. Redis):
215
+
216
+ ```typescript
217
+ import { Injectable } from '@nestjs/common';
218
+ import { OAuthStateStorePort } from '@devoven/oauth';
219
+
220
+ @Injectable()
221
+ export class RedisStateStore implements OAuthStateStorePort {
222
+ async generate(): Promise<string> {
223
+ const state = crypto.randomUUID();
224
+ await redis.set(`oauth:state:${state}`, '1', 'EX', 300);
225
+ return state;
226
+ }
227
+
228
+ async verify(state: string): Promise<boolean> {
229
+ const key = `oauth:state:${state}`;
230
+ const exists = await redis.del(key);
231
+ return exists === 1;
232
+ }
233
+ }
234
+ ```
235
+
236
+ ```typescript
237
+ OAuthModule.register({
238
+ providers: [...],
239
+ stateStore: RedisStateStore,
240
+ })
241
+ ```
242
+
243
+ ### Custom provider
244
+
245
+ Implement `OAuthProviderPort` to add any OAuth 2.0 / OIDC provider:
246
+
247
+ ```typescript
248
+ import { OAuthProviderPort, OAuthProviderConfig, OAuthProfile, OAuthToken } from '@devoven/oauth';
249
+
250
+ export class GitHubOAuthProvider implements OAuthProviderPort {
251
+ readonly name = 'github';
252
+
253
+ constructor(private readonly config: OAuthProviderConfig) {}
254
+
255
+ async getAuthorizationUrl(state: string): Promise<string> { /* ... */ }
256
+ async exchangeCode(code: string): Promise<OAuthToken> { /* ... */ }
257
+ async getProfile(token: OAuthToken): Promise<OAuthProfile> { /* ... */ }
258
+ }
259
+ ```
260
+
261
+ Pass it either as a config object (the module will construct it) or as an already-instantiated object:
262
+
263
+ ```typescript
264
+ // Config object — module constructs the instance
265
+ OAuthModule.register({
266
+ providers: [
267
+ {
268
+ name: 'github',
269
+ provider: GitHubOAuthProvider,
270
+ clientId: '...',
271
+ clientSecret: '...',
272
+ callbackUrl: 'https://example.com/oauth/github/callback',
273
+ },
274
+ ],
275
+ })
276
+
277
+ // Pre-constructed instance
278
+ OAuthModule.register({
279
+ providers: [new GitHubOAuthProvider({ clientId: '...', clientSecret: '...', callbackUrl: '...', scopes: [] })],
280
+ })
281
+ ```
@@ -0,0 +1,3 @@
1
+ export interface GetAuthorizationUrlPort {
2
+ execute(provider: string): Promise<string>;
3
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=get-auth-url.port.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-auth-url.port.js","sourceRoot":"","sources":["../../../src/application/ports/get-auth-url.port.ts"],"names":[],"mappings":""}
@@ -0,0 +1,4 @@
1
+ import { OAuthProfile } from "../../domain/entities/oauth-profile.entity";
2
+ export interface HandleCallbackPort {
3
+ execute(provider: string, code: string, state: string): Promise<OAuthProfile>;
4
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=handle-callback.port.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handle-callback.port.js","sourceRoot":"","sources":["../../../src/application/ports/handle-callback.port.ts"],"names":[],"mappings":""}
@@ -0,0 +1,15 @@
1
+ import { OAuthProfile } from "../../domain/entities/oauth-profile.entity";
2
+ import { OAuthToken } from "../../domain/value-objects/oauth-token.vo";
3
+ export interface OAuthProviderPort {
4
+ readonly name: string;
5
+ getAuthorizationUrl(state: string): Promise<string>;
6
+ exchangeCode(code: string): Promise<OAuthToken>;
7
+ getProfile(token: OAuthToken): Promise<OAuthProfile>;
8
+ }
9
+ export interface OAuthProviderConfig {
10
+ clientId: string;
11
+ clientSecret: string;
12
+ callbackUrl: string;
13
+ scopes: string[];
14
+ }
15
+ export type OAuthProviderRegistry = Map<string, OAuthProviderPort>;
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=oauth-provider.port.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-provider.port.js","sourceRoot":"","sources":["../../../src/application/ports/oauth-provider.port.ts"],"names":[],"mappings":""}
@@ -0,0 +1,4 @@
1
+ export interface OAuthStateStorePort {
2
+ generate(): Promise<string>;
3
+ verify(state: string): Promise<boolean>;
4
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=oauth-state-store.port.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-state-store.port.js","sourceRoot":"","sources":["../../../src/application/ports/oauth-state-store.port.ts"],"names":[],"mappings":""}
@@ -0,0 +1,9 @@
1
+ import { OAuthProviderRegistry } from '../ports/oauth-provider.port';
2
+ import { OAuthStateStorePort } from '../ports/oauth-state-store.port';
3
+ import { GetAuthorizationUrlPort } from '../ports/get-auth-url.port';
4
+ export declare class GetAuthorizationUrlUseCase implements GetAuthorizationUrlPort {
5
+ private readonly providers;
6
+ private readonly stateStore;
7
+ constructor(providers: OAuthProviderRegistry, stateStore: OAuthStateStorePort);
8
+ execute(provider: string): Promise<string>;
9
+ }
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.GetAuthorizationUrlUseCase = void 0;
16
+ const common_1 = require("@nestjs/common");
17
+ let GetAuthorizationUrlUseCase = class GetAuthorizationUrlUseCase {
18
+ constructor(providers, stateStore) {
19
+ this.providers = providers;
20
+ this.stateStore = stateStore;
21
+ }
22
+ async execute(provider) {
23
+ const oauthProvider = this.providers.get(provider);
24
+ if (!oauthProvider) {
25
+ throw new Error(`Unknown OAuth provider: ${provider}`);
26
+ }
27
+ const state = await this.stateStore.generate();
28
+ return oauthProvider.getAuthorizationUrl(state);
29
+ }
30
+ };
31
+ exports.GetAuthorizationUrlUseCase = GetAuthorizationUrlUseCase;
32
+ exports.GetAuthorizationUrlUseCase = GetAuthorizationUrlUseCase = __decorate([
33
+ (0, common_1.Injectable)(),
34
+ __param(0, (0, common_1.Inject)('OAuthProviderRegistry')),
35
+ __param(1, (0, common_1.Inject)('OAuthStateStorePort')),
36
+ __metadata("design:paramtypes", [Object, Object])
37
+ ], GetAuthorizationUrlUseCase);
38
+ //# sourceMappingURL=get-auth-url.use-case.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-auth-url.use-case.js","sourceRoot":"","sources":["../../../src/application/use-cases/get-auth-url.use-case.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAAmD;AAM5C,IAAM,0BAA0B,GAAhC,MAAM,0BAA0B;IACnC,YAEqB,SAAgC,EAGhC,UAA+B;QAH/B,cAAS,GAAT,SAAS,CAAuB;QAGhC,eAAU,GAAV,UAAU,CAAqB;IAChD,CAAC;IAEL,KAAK,CAAC,OAAO,CAAC,QAAgB;QAC1B,MAAM,aAAa,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;QAElD,IAAI,CAAC,aAAa,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,2BAA2B,QAAQ,EAAE,CAAC,CAAA;QAC1D,CAAC;QAED,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAA;QAE9C,OAAO,aAAa,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAA;IACnD,CAAC;CACJ,CAAA;AApBY,gEAA0B;qCAA1B,0BAA0B;IADtC,IAAA,mBAAU,GAAE;IAGJ,WAAA,IAAA,eAAM,EAAC,uBAAuB,CAAC,CAAA;IAG/B,WAAA,IAAA,eAAM,EAAC,qBAAqB,CAAC,CAAA;;GALzB,0BAA0B,CAoBtC"}
@@ -0,0 +1,10 @@
1
+ import { HandleCallbackPort } from "../ports/handle-callback.port";
2
+ import { OAuthProviderRegistry } from "../ports/oauth-provider.port";
3
+ import { OAuthStateStorePort } from "../ports/oauth-state-store.port";
4
+ import { OAuthProfile } from "../../domain/entities/oauth-profile.entity";
5
+ export declare class HandleCallbackUseCase implements HandleCallbackPort {
6
+ private readonly providers;
7
+ private readonly stateStore;
8
+ constructor(providers: OAuthProviderRegistry, stateStore: OAuthStateStorePort);
9
+ execute(provider: string, code: string, state: string): Promise<OAuthProfile>;
10
+ }
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.HandleCallbackUseCase = void 0;
16
+ const common_1 = require("@nestjs/common");
17
+ let HandleCallbackUseCase = class HandleCallbackUseCase {
18
+ constructor(providers, stateStore) {
19
+ this.providers = providers;
20
+ this.stateStore = stateStore;
21
+ }
22
+ async execute(provider, code, state) {
23
+ const valid = await this.stateStore.verify(state);
24
+ if (!valid) {
25
+ throw new common_1.BadRequestException('Invalid or expired OAuth state');
26
+ }
27
+ const oauthProvider = this.providers.get(provider);
28
+ if (!oauthProvider) {
29
+ throw new common_1.BadRequestException(`Unknown OAuth provider: ${provider}`);
30
+ }
31
+ const token = await oauthProvider.exchangeCode(code);
32
+ return oauthProvider.getProfile(token);
33
+ }
34
+ };
35
+ exports.HandleCallbackUseCase = HandleCallbackUseCase;
36
+ exports.HandleCallbackUseCase = HandleCallbackUseCase = __decorate([
37
+ (0, common_1.Injectable)(),
38
+ __param(0, (0, common_1.Inject)('OAuthProviderRegistry')),
39
+ __param(1, (0, common_1.Inject)('OAuthStateStorePort')),
40
+ __metadata("design:paramtypes", [Object, Object])
41
+ ], HandleCallbackUseCase);
42
+ //# sourceMappingURL=handle-callback.use-case.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handle-callback.use-case.js","sourceRoot":"","sources":["../../../src/application/use-cases/handle-callback.use-case.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAAyE;AAOlE,IAAM,qBAAqB,GAA3B,MAAM,qBAAqB;IAC9B,YAEqB,SAAgC,EAGhC,UAA+B;QAH/B,cAAS,GAAT,SAAS,CAAuB;QAGhC,eAAU,GAAV,UAAU,CAAqB;IAChD,CAAC;IAEL,KAAK,CAAC,OAAO,CAAC,QAAgB,EAAE,IAAY,EAAE,KAAa;QACvD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QAEjD,IAAI,CAAC,KAAK,EAAE,CAAC;YACT,MAAM,IAAI,4BAAmB,CAAC,gCAAgC,CAAC,CAAA;QACnE,CAAC;QAED,MAAM,aAAa,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;QAElD,IAAI,CAAC,aAAa,EAAE,CAAC;YACjB,MAAM,IAAI,4BAAmB,CAAC,2BAA2B,QAAQ,EAAE,CAAC,CAAA;QACxE,CAAC;QAED,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,YAAY,CAAC,IAAI,CAAC,CAAA;QAEpD,OAAO,aAAa,CAAC,UAAU,CAAC,KAAK,CAAC,CAAA;IAC1C,CAAC;CACJ,CAAA;AA1BY,sDAAqB;gCAArB,qBAAqB;IADjC,IAAA,mBAAU,GAAE;IAGJ,WAAA,IAAA,eAAM,EAAC,uBAAuB,CAAC,CAAA;IAG/B,WAAA,IAAA,eAAM,EAAC,qBAAqB,CAAC,CAAA;;GALzB,qBAAqB,CA0BjC"}
@@ -0,0 +1,16 @@
1
+ export declare class OAuthProfile {
2
+ readonly providerId: string;
3
+ readonly provider: string;
4
+ readonly email: string | null;
5
+ readonly name: string | null;
6
+ readonly avatarUrl: string | null;
7
+ private constructor();
8
+ static create(params: {
9
+ providerId: string;
10
+ provider: string;
11
+ email: string | null;
12
+ name: string | null;
13
+ avatarUrl: string | null;
14
+ }): OAuthProfile;
15
+ get id(): string;
16
+ }
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OAuthProfile = void 0;
4
+ class OAuthProfile {
5
+ constructor(params) {
6
+ this.provider = params.provider;
7
+ this.providerId = params.providerId;
8
+ this.email = params.email;
9
+ this.name = params.name;
10
+ this.avatarUrl = params.avatarUrl;
11
+ }
12
+ static create(params) {
13
+ if (!params.providerId) {
14
+ throw new Error('providerId is required');
15
+ }
16
+ if (!params.provider || params.provider.trim().length === 0) {
17
+ throw new Error('provider cannot be empty');
18
+ }
19
+ return new OAuthProfile(params);
20
+ }
21
+ get id() {
22
+ return `${this.provider}:${this.providerId}`;
23
+ }
24
+ }
25
+ exports.OAuthProfile = OAuthProfile;
26
+ //# sourceMappingURL=oauth-profile.entity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-profile.entity.js","sourceRoot":"","sources":["../../../src/domain/entities/oauth-profile.entity.ts"],"names":[],"mappings":";;;AAAA,MAAa,YAAY;IAOrB,YAAoB,MAMnB;QACG,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAA;QAC/B,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,CAAA;QACnC,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAA;QACzB,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAA;QACvB,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAA;IACrC,CAAC;IAED,MAAM,CAAC,MAAM,CAAC,MAMb;QACG,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAA;QAC7C,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1D,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAA;QAC/C,CAAC;QAED,OAAO,IAAI,YAAY,CAAC,MAAM,CAAC,CAAA;IACnC,CAAC;IAED,IAAI,EAAE;QACF,OAAO,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,UAAU,EAAE,CAAA;IAChD,CAAC;CACJ;AA1CD,oCA0CC"}
@@ -0,0 +1,15 @@
1
+ export declare class OAuthToken {
2
+ readonly accessToken: string;
3
+ readonly refreshToken: string | null;
4
+ readonly expiresAt: Date | null;
5
+ readonly idToken: string | null;
6
+ private constructor();
7
+ get isExpired(): boolean;
8
+ static create(params: {
9
+ accessToken: string;
10
+ refreshToken: string | null;
11
+ expiresIn: number | null;
12
+ expiresAt: Date | null;
13
+ idToken: string | null;
14
+ }): OAuthToken;
15
+ }
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OAuthToken = void 0;
4
+ class OAuthToken {
5
+ constructor(accessToken, refreshToken, expiresAt, idToken) {
6
+ this.accessToken = accessToken;
7
+ this.refreshToken = refreshToken;
8
+ this.expiresAt = expiresAt;
9
+ this.idToken = idToken;
10
+ }
11
+ get isExpired() {
12
+ return this.expiresAt ? Date.now() > this.expiresAt.getTime() : false;
13
+ }
14
+ static create(params) {
15
+ if (!params.accessToken) {
16
+ throw new Error("accessToken is required");
17
+ }
18
+ const expiresAt = params.expiresAt ??
19
+ (params.expiresIn
20
+ ? new Date(Date.now() + params.expiresIn * 1000)
21
+ : null);
22
+ return new OAuthToken(params.accessToken, params.refreshToken, expiresAt, params.idToken);
23
+ }
24
+ }
25
+ exports.OAuthToken = OAuthToken;
26
+ //# sourceMappingURL=oauth-token.vo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-token.vo.js","sourceRoot":"","sources":["../../../src/domain/value-objects/oauth-token.vo.ts"],"names":[],"mappings":";;;AAAA,MAAa,UAAU;IAMnB,YACI,WAAmB,EACnB,YAA2B,EAC3B,SAAsB,EACtB,OAAsB;QAEtB,IAAI,CAAC,WAAW,GAAG,WAAW,CAAA;QAC9B,IAAI,CAAC,YAAY,GAAG,YAAY,CAAA;QAChC,IAAI,CAAC,SAAS,GAAG,SAAS,CAAA;QAC1B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;IAC1B,CAAC;IAED,IAAI,SAAS;QACT,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;IAC1E,CAAC;IAED,MAAM,CAAC,MAAM,CAAC,MAMb;QACG,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAA;QAC9C,CAAC;QAED,MAAM,SAAS,GACX,MAAM,CAAC,SAAS;YAChB,CAAC,MAAM,CAAC,SAAS;gBACb,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,SAAS,GAAG,IAAI,CAAC;gBAChD,CAAC,CAAC,IAAI,CAAC,CAAA;QAEf,OAAO,IAAI,UAAU,CACjB,MAAM,CAAC,WAAW,EAClB,MAAM,CAAC,YAAY,EACnB,SAAS,EACT,MAAM,CAAC,OAAO,CACjB,CAAA;IACL,CAAC;CACJ;AA9CD,gCA8CC"}
@@ -0,0 +1,13 @@
1
+ export { OAuthModule } from './oauth.module';
2
+ export { OAuthModuleOptions, OAuthProviderOption } from './oauth.module-definition';
3
+ export { OAuthProfile } from './domain/entities/oauth-profile.entity';
4
+ export { OAuthToken } from './domain/value-objects/oauth-token.vo';
5
+ export { GetAuthorizationUrlPort } from './application/ports/get-auth-url.port';
6
+ export { HandleCallbackPort } from './application/ports/handle-callback.port';
7
+ export { OAuthProviderPort, OAuthProviderConfig, OAuthProviderRegistry, } from './application/ports/oauth-provider.port';
8
+ export { OAuthStateStorePort } from './application/ports/oauth-state-store.port';
9
+ export { OAuthCallbackQueryDto } from './presentation/dto/oauth-callback-query.dto';
10
+ export { OAuthProfileResponseDto } from './presentation/dto/oauth-profile-response.dto';
11
+ export { GoogleOAuthProvider } from './infrastructure/providers/google-oauth.provider';
12
+ export { LineOAuthProvider } from './infrastructure/providers/line-oauth.provider';
13
+ export { InMemoryStateStore } from './infrastructure/state/in-memory-state-store';
package/dist/index.js ADDED
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InMemoryStateStore = exports.LineOAuthProvider = exports.GoogleOAuthProvider = exports.OAuthProfileResponseDto = exports.OAuthCallbackQueryDto = exports.OAuthToken = exports.OAuthProfile = exports.OAuthModule = void 0;
4
+ var oauth_module_1 = require("./oauth.module");
5
+ Object.defineProperty(exports, "OAuthModule", { enumerable: true, get: function () { return oauth_module_1.OAuthModule; } });
6
+ var oauth_profile_entity_1 = require("./domain/entities/oauth-profile.entity");
7
+ Object.defineProperty(exports, "OAuthProfile", { enumerable: true, get: function () { return oauth_profile_entity_1.OAuthProfile; } });
8
+ var oauth_token_vo_1 = require("./domain/value-objects/oauth-token.vo");
9
+ Object.defineProperty(exports, "OAuthToken", { enumerable: true, get: function () { return oauth_token_vo_1.OAuthToken; } });
10
+ var oauth_callback_query_dto_1 = require("./presentation/dto/oauth-callback-query.dto");
11
+ Object.defineProperty(exports, "OAuthCallbackQueryDto", { enumerable: true, get: function () { return oauth_callback_query_dto_1.OAuthCallbackQueryDto; } });
12
+ var oauth_profile_response_dto_1 = require("./presentation/dto/oauth-profile-response.dto");
13
+ Object.defineProperty(exports, "OAuthProfileResponseDto", { enumerable: true, get: function () { return oauth_profile_response_dto_1.OAuthProfileResponseDto; } });
14
+ var google_oauth_provider_1 = require("./infrastructure/providers/google-oauth.provider");
15
+ Object.defineProperty(exports, "GoogleOAuthProvider", { enumerable: true, get: function () { return google_oauth_provider_1.GoogleOAuthProvider; } });
16
+ var line_oauth_provider_1 = require("./infrastructure/providers/line-oauth.provider");
17
+ Object.defineProperty(exports, "LineOAuthProvider", { enumerable: true, get: function () { return line_oauth_provider_1.LineOAuthProvider; } });
18
+ var in_memory_state_store_1 = require("./infrastructure/state/in-memory-state-store");
19
+ Object.defineProperty(exports, "InMemoryStateStore", { enumerable: true, get: function () { return in_memory_state_store_1.InMemoryStateStore; } });
20
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,+CAA4C;AAAnC,2GAAA,WAAW,OAAA;AAGpB,+EAAqE;AAA5D,oHAAA,YAAY,OAAA;AACrB,wEAAkE;AAAzD,4GAAA,UAAU,OAAA;AAWnB,wFAAmF;AAA1E,iIAAA,qBAAqB,OAAA;AAC9B,4FAAuF;AAA9E,qIAAA,uBAAuB,OAAA;AAEhC,0FAAsF;AAA7E,4HAAA,mBAAmB,OAAA;AAC5B,sFAAkF;AAAzE,wHAAA,iBAAiB,OAAA;AAC1B,sFAAiF;AAAxE,2HAAA,kBAAkB,OAAA"}
@@ -0,0 +1,15 @@
1
+ import { OAuthProviderConfig, OAuthProviderPort } from "../../application/ports/oauth-provider.port";
2
+ import { OAuthProfile } from "../../domain/entities/oauth-profile.entity";
3
+ import { OAuthToken } from "../../domain/value-objects/oauth-token.vo";
4
+ export declare class GoogleOAuthProvider<T extends OAuthProviderConfig = OAuthProviderConfig> implements OAuthProviderPort {
5
+ readonly name = "google";
6
+ private readonly AUTHORIZE_URL;
7
+ private readonly TOKEN_URL;
8
+ private readonly USERINFO_URL;
9
+ private readonly authClient;
10
+ protected readonly props: T;
11
+ constructor(props: T);
12
+ getAuthorizationUrl(state: string): Promise<string>;
13
+ exchangeCode(code: string): Promise<OAuthToken>;
14
+ getProfile(token: OAuthToken): Promise<OAuthProfile>;
15
+ }