@fnd-platform/cognito-auth 1.0.0-alpha.1
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/LICENSE +21 -0
- package/README.md +323 -0
- package/lib/authorizer/handler.d.ts +33 -0
- package/lib/authorizer/handler.d.ts.map +1 -0
- package/lib/authorizer/handler.js +106 -0
- package/lib/authorizer/handler.js.map +1 -0
- package/lib/authorizer/index.d.ts +7 -0
- package/lib/authorizer/index.d.ts.map +1 -0
- package/lib/authorizer/index.js +16 -0
- package/lib/authorizer/index.js.map +1 -0
- package/lib/client/auth-client.d.ts +131 -0
- package/lib/client/auth-client.d.ts.map +1 -0
- package/lib/client/auth-client.js +270 -0
- package/lib/client/auth-client.js.map +1 -0
- package/lib/client/errors.d.ts +67 -0
- package/lib/client/errors.d.ts.map +1 -0
- package/lib/client/errors.js +90 -0
- package/lib/client/errors.js.map +1 -0
- package/lib/client/index.d.ts +8 -0
- package/lib/client/index.d.ts.map +1 -0
- package/lib/client/index.js +29 -0
- package/lib/client/index.js.map +1 -0
- package/lib/cognito-construct.d.ts +113 -0
- package/lib/cognito-construct.d.ts.map +1 -0
- package/lib/cognito-construct.js +211 -0
- package/lib/cognito-construct.js.map +1 -0
- package/lib/index.d.ts +30 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +59 -0
- package/lib/index.js.map +1 -0
- package/lib/jwt.d.ts +89 -0
- package/lib/jwt.d.ts.map +1 -0
- package/lib/jwt.js +117 -0
- package/lib/jwt.js.map +1 -0
- package/lib/middleware/auth.d.ts +59 -0
- package/lib/middleware/auth.d.ts.map +1 -0
- package/lib/middleware/auth.js +148 -0
- package/lib/middleware/auth.js.map +1 -0
- package/lib/middleware/index.d.ts +12 -0
- package/lib/middleware/index.d.ts.map +1 -0
- package/lib/middleware/index.js +16 -0
- package/lib/middleware/index.js.map +1 -0
- package/lib/remix/admin.server.d.ts +105 -0
- package/lib/remix/admin.server.d.ts.map +1 -0
- package/lib/remix/admin.server.js +146 -0
- package/lib/remix/admin.server.js.map +1 -0
- package/lib/remix/index.d.ts +17 -0
- package/lib/remix/index.d.ts.map +1 -0
- package/lib/remix/index.js +95 -0
- package/lib/remix/index.js.map +1 -0
- package/lib/remix/session.server.d.ts +177 -0
- package/lib/remix/session.server.d.ts.map +1 -0
- package/lib/remix/session.server.js +287 -0
- package/lib/remix/session.server.js.map +1 -0
- package/lib/types.d.ts +161 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +8 -0
- package/lib/types.js.map +1 -0
- package/lib/utils/index.d.ts +12 -0
- package/lib/utils/index.d.ts.map +1 -0
- package/lib/utils/index.js +22 -0
- package/lib/utils/index.js.map +1 -0
- package/lib/utils/token-refresh.d.ts +62 -0
- package/lib/utils/token-refresh.d.ts.map +1 -0
- package/lib/utils/token-refresh.js +84 -0
- package/lib/utils/token-refresh.js.map +1 -0
- package/package.json +70 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 fnd-platform contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
# @fnd-platform/cognito-auth
|
|
2
|
+
|
|
3
|
+
AWS Cognito authentication constructs and middleware for fnd-platform applications. Provides CDK constructs, JWT validation, Lambda authorizers, and Remix authentication utilities.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @fnd-platform/cognito-auth
|
|
9
|
+
# or
|
|
10
|
+
pnpm add @fnd-platform/cognito-auth
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
### CDK Construct
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { FndCognitoAuth } from '@fnd-platform/cognito-auth';
|
|
19
|
+
|
|
20
|
+
const auth = new FndCognitoAuth(this, 'Auth', {
|
|
21
|
+
stage: 'dev',
|
|
22
|
+
appName: 'my-app',
|
|
23
|
+
callbackUrls: ['http://localhost:3000/auth/callback'],
|
|
24
|
+
logoutUrls: ['http://localhost:3000'],
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Access outputs
|
|
28
|
+
auth.userPool;
|
|
29
|
+
auth.userPoolClient;
|
|
30
|
+
auth.identityPool;
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Lambda Middleware
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { withCognitoAuth } from '@fnd-platform/cognito-auth';
|
|
37
|
+
|
|
38
|
+
export const handler = withCognitoAuth({
|
|
39
|
+
userPoolId: process.env.USER_POOL_ID!,
|
|
40
|
+
clientId: process.env.CLIENT_ID!,
|
|
41
|
+
})(async (event) => {
|
|
42
|
+
// event.requestContext.authorizer contains user claims
|
|
43
|
+
const userId = event.requestContext.authorizer?.claims?.sub;
|
|
44
|
+
return { statusCode: 200, body: JSON.stringify({ userId }) };
|
|
45
|
+
});
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Remix Authentication
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
import { requireAuth, getOptionalUser, logout } from '@fnd-platform/cognito-auth';
|
|
52
|
+
|
|
53
|
+
// In a loader - require authentication
|
|
54
|
+
export const loader = async ({ request }) => {
|
|
55
|
+
const user = await requireAuth(request);
|
|
56
|
+
return json({ user });
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// In a loader - optional authentication
|
|
60
|
+
export const loader = async ({ request }) => {
|
|
61
|
+
const user = await getOptionalUser(request);
|
|
62
|
+
return json({ user });
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// Logout action
|
|
66
|
+
export const action = async ({ request }) => {
|
|
67
|
+
return logout(request);
|
|
68
|
+
};
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Features
|
|
72
|
+
|
|
73
|
+
- **CDK Construct** - Fully configured Cognito User Pool with best practices
|
|
74
|
+
- **JWT Validation** - Verify and extract claims from Cognito tokens
|
|
75
|
+
- **Lambda Middleware** - Authenticate Lambda handlers
|
|
76
|
+
- **Lambda Authorizer** - API Gateway authorizer function
|
|
77
|
+
- **Token Refresh** - Automatic token refresh utilities
|
|
78
|
+
- **Remix Integration** - Session management and auth utilities for Remix
|
|
79
|
+
|
|
80
|
+
## CDK Construct Options
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
interface FndCognitoAuthProps {
|
|
84
|
+
stage: 'dev' | 'staging' | 'prod';
|
|
85
|
+
appName: string;
|
|
86
|
+
callbackUrls: string[];
|
|
87
|
+
logoutUrls: string[];
|
|
88
|
+
|
|
89
|
+
// Optional
|
|
90
|
+
selfSignUp?: boolean; // Allow self-registration (default: true)
|
|
91
|
+
mfa?: 'off' | 'optional' | 'required'; // MFA setting (default: 'optional')
|
|
92
|
+
passwordPolicy?: {
|
|
93
|
+
minLength?: number; // Default: 8
|
|
94
|
+
requireUppercase?: boolean; // Default: true
|
|
95
|
+
requireLowercase?: boolean; // Default: true
|
|
96
|
+
requireDigits?: boolean; // Default: true
|
|
97
|
+
requireSymbols?: boolean; // Default: true
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Examples
|
|
103
|
+
|
|
104
|
+
### Role-Based Access Control
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
import { requireRole, requireAdmin, hasRole } from '@fnd-platform/cognito-auth';
|
|
108
|
+
|
|
109
|
+
// Require admin role
|
|
110
|
+
export const loader = async ({ request }) => {
|
|
111
|
+
const user = await requireAdmin(request);
|
|
112
|
+
// Only admins can reach this point
|
|
113
|
+
return json({ user });
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
// Require specific roles
|
|
117
|
+
export const loader = async ({ request }) => {
|
|
118
|
+
const user = await requireRole(request, ['admin', 'editor']);
|
|
119
|
+
return json({ user });
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
// Check role without redirecting
|
|
123
|
+
export const loader = async ({ request }) => {
|
|
124
|
+
const user = await requireAuth(request);
|
|
125
|
+
const isAdmin = hasRole(user, 'admin');
|
|
126
|
+
return json({ user, isAdmin });
|
|
127
|
+
};
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### JWT Verification
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
import { verifyToken, verifyAndExtract, getVerifier } from '@fnd-platform/cognito-auth';
|
|
134
|
+
|
|
135
|
+
// Verify a token
|
|
136
|
+
const isValid = await verifyToken(token, {
|
|
137
|
+
userPoolId: process.env.USER_POOL_ID!,
|
|
138
|
+
clientId: process.env.CLIENT_ID!,
|
|
139
|
+
tokenUse: 'access',
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// Verify and get claims
|
|
143
|
+
const claims = await verifyAndExtract(token, {
|
|
144
|
+
userPoolId: process.env.USER_POOL_ID!,
|
|
145
|
+
clientId: process.env.CLIENT_ID!,
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Get a reusable verifier (cached)
|
|
149
|
+
const verifier = getVerifier({
|
|
150
|
+
userPoolId: process.env.USER_POOL_ID!,
|
|
151
|
+
clientId: process.env.CLIENT_ID!,
|
|
152
|
+
});
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Session Management
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
import {
|
|
159
|
+
createSessionStorage,
|
|
160
|
+
getSession,
|
|
161
|
+
createUserSession,
|
|
162
|
+
getUserSession,
|
|
163
|
+
} from '@fnd-platform/cognito-auth';
|
|
164
|
+
|
|
165
|
+
// Create session storage
|
|
166
|
+
const sessionStorage = createSessionStorage({
|
|
167
|
+
secret: process.env.SESSION_SECRET!,
|
|
168
|
+
secure: process.env.NODE_ENV === 'production',
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// Create a user session after login
|
|
172
|
+
export const action = async ({ request }) => {
|
|
173
|
+
const tokens = await authenticateUser(credentials);
|
|
174
|
+
return createUserSession(request, {
|
|
175
|
+
accessToken: tokens.accessToken,
|
|
176
|
+
idToken: tokens.idToken,
|
|
177
|
+
refreshToken: tokens.refreshToken,
|
|
178
|
+
redirectTo: '/dashboard',
|
|
179
|
+
});
|
|
180
|
+
};
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Auth Client
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
import { FndAuthClient, AuthError } from '@fnd-platform/cognito-auth';
|
|
187
|
+
|
|
188
|
+
const authClient = new FndAuthClient({
|
|
189
|
+
userPoolId: process.env.USER_POOL_ID!,
|
|
190
|
+
clientId: process.env.CLIENT_ID!,
|
|
191
|
+
region: process.env.AWS_REGION!,
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
try {
|
|
195
|
+
// Sign up
|
|
196
|
+
const result = await authClient.signUp({
|
|
197
|
+
email: 'user@example.com',
|
|
198
|
+
password: 'SecurePassword123!',
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// Sign in
|
|
202
|
+
const tokens = await authClient.signIn({
|
|
203
|
+
email: 'user@example.com',
|
|
204
|
+
password: 'SecurePassword123!',
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
// Refresh tokens
|
|
208
|
+
const newTokens = await authClient.refreshTokens(tokens.refreshToken);
|
|
209
|
+
} catch (error) {
|
|
210
|
+
if (error instanceof AuthError) {
|
|
211
|
+
console.error(error.code, error.message);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## API Reference
|
|
217
|
+
|
|
218
|
+
See the [full API documentation](../../docs/api/modules/cognito_auth_src.html) for detailed type definitions and examples.
|
|
219
|
+
|
|
220
|
+
### CDK Constructs
|
|
221
|
+
|
|
222
|
+
- `FndCognitoAuth` - Main Cognito User Pool construct
|
|
223
|
+
- `FndCognitoAuthProps` - Construct configuration
|
|
224
|
+
- `Stage` - Valid stage values type
|
|
225
|
+
- `validateStage` - Stage validation utility
|
|
226
|
+
- `VALID_STAGES` - Array of valid stages
|
|
227
|
+
|
|
228
|
+
### JWT Utilities
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
import {
|
|
232
|
+
verifyToken, // Verify token validity
|
|
233
|
+
verifyAndExtract, // Verify and return claims
|
|
234
|
+
getVerifier, // Get cached verifier instance
|
|
235
|
+
clearVerifierCache, // Clear verifier cache
|
|
236
|
+
} from '@fnd-platform/cognito-auth';
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Middleware
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
import { withCognitoAuth } from '@fnd-platform/cognito-auth';
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Lambda Authorizer
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
import { authorizerHandler } from '@fnd-platform/cognito-auth';
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Token Utilities
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
import { refreshAccessToken, clearClientCache } from '@fnd-platform/cognito-auth';
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Auth Client
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
import { FndAuthClient, AuthError, clearAuthClientCache } from '@fnd-platform/cognito-auth';
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Remix Utilities
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
import {
|
|
267
|
+
// Session management
|
|
268
|
+
createSessionStorage,
|
|
269
|
+
getSession,
|
|
270
|
+
createUserSession,
|
|
271
|
+
getUserSession,
|
|
272
|
+
// Auth helpers
|
|
273
|
+
requireAuth,
|
|
274
|
+
getOptionalUser,
|
|
275
|
+
logout,
|
|
276
|
+
// Role-based access
|
|
277
|
+
requireAdmin,
|
|
278
|
+
requireRole,
|
|
279
|
+
hasRole,
|
|
280
|
+
hasAnyRole,
|
|
281
|
+
} from '@fnd-platform/cognito-auth';
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### Types
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
import type {
|
|
288
|
+
FndCognitoAuthProps,
|
|
289
|
+
Stage,
|
|
290
|
+
CognitoAccessTokenPayload,
|
|
291
|
+
CognitoIdTokenPayload,
|
|
292
|
+
CognitoAuthOptions,
|
|
293
|
+
JwtVerifierConfig,
|
|
294
|
+
TokenVerificationResult,
|
|
295
|
+
CognitoAuthenticatedEvent,
|
|
296
|
+
CognitoMiddleware,
|
|
297
|
+
CognitoMiddlewareHandler,
|
|
298
|
+
TokenRefreshConfig,
|
|
299
|
+
RefreshResult,
|
|
300
|
+
AuthClientConfig,
|
|
301
|
+
AuthTokens,
|
|
302
|
+
SignUpResult,
|
|
303
|
+
SessionData,
|
|
304
|
+
SessionUser,
|
|
305
|
+
AuthErrorCode,
|
|
306
|
+
} from '@fnd-platform/cognito-auth';
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
## Requirements
|
|
310
|
+
|
|
311
|
+
- Node.js 20+
|
|
312
|
+
- AWS CDK v2 (for constructs)
|
|
313
|
+
- @remix-run/node (for Remix utilities)
|
|
314
|
+
|
|
315
|
+
## Related
|
|
316
|
+
|
|
317
|
+
- [@fnd-platform/api](../api/README.md) - API handlers with auth middleware
|
|
318
|
+
- [@fnd-platform/frontend](../frontend/README.md) - Frontend with auth integration
|
|
319
|
+
- [@fnd-platform/constructs](../constructs/README.md) - CDK constructs
|
|
320
|
+
|
|
321
|
+
## License
|
|
322
|
+
|
|
323
|
+
MIT
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lambda authorizer for API Gateway.
|
|
3
|
+
*
|
|
4
|
+
* This handler validates JWT tokens and returns an IAM policy
|
|
5
|
+
* allowing or denying access to the API.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
import type { APIGatewayTokenAuthorizerHandler } from 'aws-lambda';
|
|
10
|
+
/**
|
|
11
|
+
* Lambda authorizer handler.
|
|
12
|
+
*
|
|
13
|
+
* Validates JWT tokens and returns an IAM policy.
|
|
14
|
+
*
|
|
15
|
+
* Required environment variables:
|
|
16
|
+
* - COGNITO_USER_POOL_ID
|
|
17
|
+
* - COGNITO_CLIENT_ID
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* // In CDK construct
|
|
22
|
+
* const authorizer = new lambda.Function(this, 'Authorizer', {
|
|
23
|
+
* code: lambda.Code.fromAsset('../cognito-auth/dist'),
|
|
24
|
+
* handler: 'authorizer/handler.handler',
|
|
25
|
+
* environment: {
|
|
26
|
+
* COGNITO_USER_POOL_ID: userPool.userPoolId,
|
|
27
|
+
* COGNITO_CLIENT_ID: client.userPoolClientId,
|
|
28
|
+
* },
|
|
29
|
+
* });
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare const handler: APIGatewayTokenAuthorizerHandler;
|
|
33
|
+
//# sourceMappingURL=handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../src/authorizer/handler.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,gCAAgC,EAA8B,MAAM,YAAY,CAAC;AAkD/F;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,OAAO,EAAE,gCAmCrB,CAAC"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Lambda authorizer for API Gateway.
|
|
4
|
+
*
|
|
5
|
+
* This handler validates JWT tokens and returns an IAM policy
|
|
6
|
+
* allowing or denying access to the API.
|
|
7
|
+
*
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.handler = void 0;
|
|
12
|
+
const jwt_js_1 = require("../jwt.js");
|
|
13
|
+
/**
|
|
14
|
+
* Generates an IAM policy for the authorizer response.
|
|
15
|
+
*
|
|
16
|
+
* @param principalId - User identifier
|
|
17
|
+
* @param effect - Allow or Deny
|
|
18
|
+
* @param resource - API Gateway resource ARN
|
|
19
|
+
* @param context - Additional context to pass to handlers
|
|
20
|
+
* @returns API Gateway authorizer result
|
|
21
|
+
*/
|
|
22
|
+
function generatePolicy(principalId, effect, resource, context) {
|
|
23
|
+
return {
|
|
24
|
+
principalId,
|
|
25
|
+
policyDocument: {
|
|
26
|
+
Version: '2012-10-17',
|
|
27
|
+
Statement: [
|
|
28
|
+
{
|
|
29
|
+
Action: 'execute-api:Invoke',
|
|
30
|
+
Effect: effect,
|
|
31
|
+
Resource: resource,
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
context,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Extracts the base resource ARN for wildcard matching.
|
|
40
|
+
* Allows access to all methods and paths under the API.
|
|
41
|
+
*
|
|
42
|
+
* @param methodArn - The specific method ARN from the event
|
|
43
|
+
* @returns Wildcard ARN for the API
|
|
44
|
+
*/
|
|
45
|
+
function getWildcardResource(methodArn) {
|
|
46
|
+
// methodArn format: arn:aws:execute-api:{region}:{accountId}:{apiId}/{stage}/{method}/{path}
|
|
47
|
+
const arnParts = methodArn.split(':');
|
|
48
|
+
const apiGatewayParts = arnParts[5].split('/');
|
|
49
|
+
// Return wildcard for all methods and paths: apiId/stage/*
|
|
50
|
+
return `${arnParts.slice(0, 5).join(':')}:${apiGatewayParts[0]}/${apiGatewayParts[1]}/*`;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Lambda authorizer handler.
|
|
54
|
+
*
|
|
55
|
+
* Validates JWT tokens and returns an IAM policy.
|
|
56
|
+
*
|
|
57
|
+
* Required environment variables:
|
|
58
|
+
* - COGNITO_USER_POOL_ID
|
|
59
|
+
* - COGNITO_CLIENT_ID
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* // In CDK construct
|
|
64
|
+
* const authorizer = new lambda.Function(this, 'Authorizer', {
|
|
65
|
+
* code: lambda.Code.fromAsset('../cognito-auth/dist'),
|
|
66
|
+
* handler: 'authorizer/handler.handler',
|
|
67
|
+
* environment: {
|
|
68
|
+
* COGNITO_USER_POOL_ID: userPool.userPoolId,
|
|
69
|
+
* COGNITO_CLIENT_ID: client.userPoolClientId,
|
|
70
|
+
* },
|
|
71
|
+
* });
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
const handler = async (event) => {
|
|
75
|
+
const userPoolId = process.env.COGNITO_USER_POOL_ID;
|
|
76
|
+
const clientId = process.env.COGNITO_CLIENT_ID;
|
|
77
|
+
if (!userPoolId || !clientId) {
|
|
78
|
+
console.error('Missing required environment variables');
|
|
79
|
+
throw new Error('Unauthorized');
|
|
80
|
+
}
|
|
81
|
+
// Extract token from header
|
|
82
|
+
const token = event.authorizationToken?.replace(/^Bearer\s+/i, '');
|
|
83
|
+
if (!token) {
|
|
84
|
+
console.error('No token provided');
|
|
85
|
+
throw new Error('Unauthorized');
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
const authResult = await (0, jwt_js_1.verifyAndExtract)(token, {
|
|
89
|
+
userPoolId,
|
|
90
|
+
clientId,
|
|
91
|
+
});
|
|
92
|
+
// Generate Allow policy with user context
|
|
93
|
+
const resource = getWildcardResource(event.methodArn);
|
|
94
|
+
return generatePolicy(authResult.userId, 'Allow', resource, {
|
|
95
|
+
userId: authResult.userId,
|
|
96
|
+
email: authResult.email ?? '',
|
|
97
|
+
groups: authResult.groups.join(','),
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
console.error('Authorization failed:', error);
|
|
102
|
+
throw new Error('Unauthorized');
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
exports.handler = handler;
|
|
106
|
+
//# sourceMappingURL=handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.js","sourceRoot":"","sources":["../../src/authorizer/handler.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;AAGH,sCAA6C;AAE7C;;;;;;;;GAQG;AACH,SAAS,cAAc,CACrB,WAAmB,EACnB,MAAwB,EACxB,QAAgB,EAChB,OAAmD;IAEnD,OAAO;QACL,WAAW;QACX,cAAc,EAAE;YACd,OAAO,EAAE,YAAY;YACrB,SAAS,EAAE;gBACT;oBACE,MAAM,EAAE,oBAAoB;oBAC5B,MAAM,EAAE,MAAM;oBACd,QAAQ,EAAE,QAAQ;iBACnB;aACF;SACF;QACD,OAAO;KACR,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,SAAS,mBAAmB,CAAC,SAAiB;IAC5C,6FAA6F;IAC7F,MAAM,QAAQ,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACtC,MAAM,eAAe,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAE/C,2DAA2D;IAC3D,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,eAAe,CAAC,CAAC,CAAC,IAAI,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC;AAC3F,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACI,MAAM,OAAO,GAAqC,KAAK,EAAE,KAAK,EAAE,EAAE;IACvE,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC;IACpD,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IAE/C,IAAI,CAAC,UAAU,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC7B,OAAO,CAAC,KAAK,CAAC,wCAAwC,CAAC,CAAC;QACxD,MAAM,IAAI,KAAK,CAAC,cAAc,CAAC,CAAC;IAClC,CAAC;IAED,4BAA4B;IAC5B,MAAM,KAAK,GAAG,KAAK,CAAC,kBAAkB,EAAE,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;IAEnE,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,cAAc,CAAC,CAAC;IAClC,CAAC;IAED,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,MAAM,IAAA,yBAAgB,EAAC,KAAK,EAAE;YAC/C,UAAU;YACV,QAAQ;SACT,CAAC,CAAC;QAEH,0CAA0C;QAC1C,MAAM,QAAQ,GAAG,mBAAmB,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAEtD,OAAO,cAAc,CAAC,UAAU,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE;YAC1D,MAAM,EAAE,UAAU,CAAC,MAAM;YACzB,KAAK,EAAE,UAAU,CAAC,KAAK,IAAI,EAAE;YAC7B,MAAM,EAAE,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;SACpC,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,uBAAuB,EAAE,KAAK,CAAC,CAAC;QAC9C,MAAM,IAAI,KAAK,CAAC,cAAc,CAAC,CAAC;IAClC,CAAC;AACH,CAAC,CAAC;AAnCW,QAAA,OAAO,WAmClB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/authorizer/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* Lambda authorizer exports.
|
|
4
|
+
*
|
|
5
|
+
* @packageDocumentation
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
8
|
+
exports.handler = void 0;
|
|
9
|
+
var handler_js_1 = require('./handler.js');
|
|
10
|
+
Object.defineProperty(exports, 'handler', {
|
|
11
|
+
enumerable: true,
|
|
12
|
+
get: function () {
|
|
13
|
+
return handler_js_1.handler;
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/authorizer/index.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;AAEH,2CAAuC;AAA9B,qGAAA,OAAO,OAAA"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cognito authentication client for frontend applications.
|
|
3
|
+
*
|
|
4
|
+
* Provides methods for sign-in, sign-up, sign-out, and token refresh
|
|
5
|
+
* using AWS Cognito User Pools.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
import type { AuthClientConfig, AuthTokens, SignUpResult } from '../types.js';
|
|
10
|
+
/**
|
|
11
|
+
* Clears the client cache. Useful for testing.
|
|
12
|
+
*/
|
|
13
|
+
export declare function clearClientCache(): void;
|
|
14
|
+
/**
|
|
15
|
+
* Cognito authentication client for frontend applications.
|
|
16
|
+
*
|
|
17
|
+
* Provides methods for user authentication including sign-in, sign-up,
|
|
18
|
+
* email confirmation, token refresh, and sign-out.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* const authClient = new FndAuthClient({
|
|
23
|
+
* userPoolId: process.env.COGNITO_USER_POOL_ID!,
|
|
24
|
+
* clientId: process.env.COGNITO_CLIENT_ID!,
|
|
25
|
+
* region: process.env.AWS_REGION,
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* // Sign in
|
|
29
|
+
* const tokens = await authClient.signIn('user@example.com', 'password');
|
|
30
|
+
*
|
|
31
|
+
* // Use access token for API calls
|
|
32
|
+
* const response = await fetch('/api/data', {
|
|
33
|
+
* headers: { Authorization: `Bearer ${tokens.accessToken}` },
|
|
34
|
+
* });
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export declare class FndAuthClient {
|
|
38
|
+
private readonly client;
|
|
39
|
+
private readonly clientId;
|
|
40
|
+
/**
|
|
41
|
+
* Creates a new FndAuthClient.
|
|
42
|
+
*
|
|
43
|
+
* @param config - Configuration for the auth client
|
|
44
|
+
*/
|
|
45
|
+
constructor(config: AuthClientConfig);
|
|
46
|
+
/**
|
|
47
|
+
* Signs in a user with email and password.
|
|
48
|
+
*
|
|
49
|
+
* @param email - User's email address
|
|
50
|
+
* @param password - User's password
|
|
51
|
+
* @returns Authentication tokens
|
|
52
|
+
* @throws {AuthError} If authentication fails
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```typescript
|
|
56
|
+
* try {
|
|
57
|
+
* const tokens = await authClient.signIn('user@example.com', 'password');
|
|
58
|
+
* console.log('Logged in!', tokens.accessToken);
|
|
59
|
+
* } catch (error) {
|
|
60
|
+
* if (error instanceof AuthError && error.code === 'USER_NOT_CONFIRMED') {
|
|
61
|
+
* // Redirect to confirmation page
|
|
62
|
+
* }
|
|
63
|
+
* }
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
signIn(email: string, password: string): Promise<AuthTokens>;
|
|
67
|
+
/**
|
|
68
|
+
* Signs up a new user.
|
|
69
|
+
*
|
|
70
|
+
* @param email - User's email address
|
|
71
|
+
* @param password - User's password
|
|
72
|
+
* @param name - Optional user's name
|
|
73
|
+
* @returns Sign-up result with confirmation status
|
|
74
|
+
* @throws {AuthError} If sign-up fails
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```typescript
|
|
78
|
+
* const result = await authClient.signUp('user@example.com', 'password', 'John Doe');
|
|
79
|
+
* if (!result.userConfirmed) {
|
|
80
|
+
* // Show confirmation code input
|
|
81
|
+
* console.log(`Code sent to ${result.codeDeliveryDetails?.destination}`);
|
|
82
|
+
* }
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
signUp(email: string, password: string, name?: string): Promise<SignUpResult>;
|
|
86
|
+
/**
|
|
87
|
+
* Confirms a user's sign-up with the verification code.
|
|
88
|
+
*
|
|
89
|
+
* @param email - User's email address
|
|
90
|
+
* @param code - Verification code from email/SMS
|
|
91
|
+
* @throws {AuthError} If confirmation fails
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* ```typescript
|
|
95
|
+
* await authClient.confirmSignUp('user@example.com', '123456');
|
|
96
|
+
* // User is now confirmed, can sign in
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
confirmSignUp(email: string, code: string): Promise<void>;
|
|
100
|
+
/**
|
|
101
|
+
* Refreshes authentication tokens using a refresh token.
|
|
102
|
+
*
|
|
103
|
+
* @param refreshToken - The refresh token from a previous authentication
|
|
104
|
+
* @returns New authentication tokens
|
|
105
|
+
* @throws {AuthError} If refresh fails
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```typescript
|
|
109
|
+
* // When access token is about to expire
|
|
110
|
+
* const newTokens = await authClient.refreshTokens(tokens.refreshToken);
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
refreshTokens(refreshToken: string): Promise<AuthTokens>;
|
|
114
|
+
/**
|
|
115
|
+
* Signs out the user from all devices.
|
|
116
|
+
*
|
|
117
|
+
* This invalidates all refresh tokens for the user, effectively
|
|
118
|
+
* signing them out from all devices.
|
|
119
|
+
*
|
|
120
|
+
* @param accessToken - The user's current access token
|
|
121
|
+
* @throws {AuthError} If sign-out fails
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* ```typescript
|
|
125
|
+
* await authClient.signOut(tokens.accessToken);
|
|
126
|
+
* // User is now signed out from all devices
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
signOut(accessToken: string): Promise<void>;
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=auth-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-client.d.ts","sourceRoot":"","sources":["../../src/client/auth-client.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAUH,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AA8B9E;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgC;IACvD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAElC;;;;OAIG;gBACS,MAAM,EAAE,gBAAgB;IAKpC;;;;;;;;;;;;;;;;;;;OAmBG;IACG,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAkClE;;;;;;;;;;;;;;;;;OAiBG;IACG,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IA+BnF;;;;;;;;;;;;OAYG;IACG,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAc/D;;;;;;;;;;;;OAYG;IACG,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAkC9D;;;;;;;;;;;;;;OAcG;IACG,OAAO,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAWlD"}
|