@cedarjs/auth-dbauth-setup 0.0.4

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.
@@ -0,0 +1,207 @@
1
+ import type { APIGatewayProxyEvent, Context } from 'aws-lambda'
2
+
3
+ import { DbAuthHandler } from '@cedarjs/auth-dbauth-api'
4
+ import type { DbAuthHandlerOptions, UserType } from '@cedarjs/auth-dbauth-api'
5
+
6
+ import { cookieName } from 'src/lib/auth'
7
+ import { db } from 'src/lib/db'
8
+
9
+ export const handler = async (
10
+ event: APIGatewayProxyEvent,
11
+ context: Context
12
+ ) => {
13
+ const forgotPasswordOptions: DbAuthHandlerOptions['forgotPassword'] = {
14
+ // handler() is invoked after verifying that a user was found with the given
15
+ // username. This is where you can send the user an email with a link to
16
+ // reset their password. With the default dbAuth routes and field names, the
17
+ // URL to reset the password will be:
18
+ //
19
+ // https://example.com/reset-password?resetToken=${user.resetToken}
20
+ //
21
+ // Whatever is returned from this function will be returned from
22
+ // the `forgotPassword()` function that is destructured from `useAuth()`.
23
+ // You could use this return value to, for example, show the email
24
+ // address in a toast message so the user will know it worked and where
25
+ // to look for the email.
26
+ //
27
+ // Note that this return value is sent to the client in *plain text*
28
+ // so don't include anything you wouldn't want prying eyes to see. The
29
+ // `user` here has been sanitized to only include the fields listed in
30
+ // `allowedUserFields` so it should be safe to return as-is.
31
+ handler: (user, _resetToken) => {
32
+ // TODO: Send user an email/message with a link to reset their password,
33
+ // including the `resetToken`. The URL should look something like:
34
+ // `http://localhost:8910/reset-password?resetToken=${resetToken}`
35
+
36
+ return user
37
+ },
38
+
39
+ // How long the resetToken is valid for, in seconds (default is 24 hours)
40
+ expires: 60 * 60 * 24,
41
+
42
+ errors: {
43
+ // for security reasons you may want to be vague here rather than expose
44
+ // the fact that the email address wasn't found (prevents fishing for
45
+ // valid email addresses)
46
+ usernameNotFound: 'Username not found',
47
+ // if the user somehow gets around client validation
48
+ usernameRequired: 'Username is required',
49
+ },
50
+ }
51
+
52
+ const loginOptions: DbAuthHandlerOptions['login'] = {
53
+ // handler() is called after finding the user that matches the
54
+ // username/password provided at login, but before actually considering them
55
+ // logged in. The `user` argument will be the user in the database that
56
+ // matched the username/password.
57
+ //
58
+ // If you want to allow this user to log in simply return the user.
59
+ //
60
+ // If you want to prevent someone logging in for another reason (maybe they
61
+ // didn't validate their email yet), throw an error and it will be returned
62
+ // by the `logIn()` function from `useAuth()` in the form of:
63
+ // `{ message: 'Error message' }`
64
+ handler: (user) => {
65
+ return user
66
+ },
67
+
68
+ errors: {
69
+ usernameOrPasswordMissing: 'Both username and password are required',
70
+ usernameNotFound: 'Username ${username} not found',
71
+ // For security reasons you may want to make this the same as the
72
+ // usernameNotFound error so that a malicious user can't use the error
73
+ // to narrow down if it's the username or password that's incorrect
74
+ incorrectPassword: 'Incorrect password for ${username}',
75
+ },
76
+
77
+ // How long a user will remain logged in, in seconds
78
+ expires: 60 * 60 * 24 * 365 * 10,
79
+ }
80
+
81
+ const resetPasswordOptions: DbAuthHandlerOptions['resetPassword'] = {
82
+ // handler() is invoked after the password has been successfully updated in
83
+ // the database. Returning anything truthy will automatically log the user
84
+ // in. Return `false` otherwise, and in the Reset Password page redirect the
85
+ // user to the login page.
86
+ handler: (_user) => {
87
+ return true
88
+ },
89
+
90
+ // If `false` then the new password MUST be different from the current one
91
+ allowReusedPassword: true,
92
+
93
+ errors: {
94
+ // the resetToken is valid, but expired
95
+ resetTokenExpired: 'resetToken is expired',
96
+ // no user was found with the given resetToken
97
+ resetTokenInvalid: 'resetToken is invalid',
98
+ // the resetToken was not present in the URL
99
+ resetTokenRequired: 'resetToken is required',
100
+ // new password is the same as the old password (apparently they did not forget it)
101
+ reusedPassword: 'Must choose a new password',
102
+ },
103
+ }
104
+
105
+ interface UserAttributes {
106
+ name: string
107
+ }
108
+
109
+ const signupOptions: DbAuthHandlerOptions<
110
+ UserType,
111
+ UserAttributes
112
+ >['signup'] = {
113
+ // Whatever you want to happen to your data on new user signup. Redwood will
114
+ // check for duplicate usernames before calling this handler. At a minimum
115
+ // you need to save the `username`, `hashedPassword` and `salt` to your
116
+ // user table. `userAttributes` contains any additional object members that
117
+ // were included in the object given to the `signUp()` function you got
118
+ // from `useAuth()`.
119
+ //
120
+ // If you want the user to be immediately logged in, return the user that
121
+ // was created.
122
+ //
123
+ // If this handler throws an error, it will be returned by the `signUp()`
124
+ // function in the form of: `{ error: 'Error message' }`.
125
+ //
126
+ // If this returns anything else, it will be returned by the
127
+ // `signUp()` function in the form of: `{ message: 'String here' }`.
128
+ handler: ({
129
+ username,
130
+ hashedPassword,
131
+ salt,
132
+ userAttributes: _userAttributes,
133
+ }) => {
134
+ return db.user.create({
135
+ data: {
136
+ email: username,
137
+ hashedPassword: hashedPassword,
138
+ salt: salt,
139
+ // name: userAttributes.name
140
+ },
141
+ })
142
+ },
143
+
144
+ // Include any format checks for password here. Return `true` if the
145
+ // password is valid, otherwise throw a `PasswordValidationError`.
146
+ // Import the error along with `DbAuthHandler` from `@cedarjs/api` above.
147
+ passwordValidation: (_password) => {
148
+ return true
149
+ },
150
+
151
+ errors: {
152
+ // `field` will be either "username" or "password"
153
+ fieldMissing: '${field} is required',
154
+ usernameTaken: 'Username `${username}` already in use',
155
+ },
156
+ }
157
+
158
+ const authHandler = new DbAuthHandler(event, context, {
159
+ // Provide prisma db client
160
+ db: db,
161
+
162
+ // The name of the property you'd call on `db` to access your user table.
163
+ // i.e. if your Prisma model is named `User` this value would be `user`, as in `db.user`
164
+ authModelAccessor: 'user',
165
+
166
+ // A map of what dbAuth calls a field to what your database calls it.
167
+ // `id` is whatever column you use to uniquely identify a user (probably
168
+ // something like `id` or `userId` or even `email`)
169
+ authFields: {
170
+ id: 'id',
171
+ username: 'email',
172
+ hashedPassword: 'hashedPassword',
173
+ salt: 'salt',
174
+ resetToken: 'resetToken',
175
+ resetTokenExpiresAt: 'resetTokenExpiresAt',
176
+ },
177
+
178
+ // A list of fields on your user object that are safe to return to the
179
+ // client when invoking a handler that returns a user (like forgotPassword
180
+ // and signup). This list should be as small as possible to be sure not to
181
+ // leak any sensitive information to the client.
182
+ allowedUserFields: ['id', 'email'],
183
+
184
+ // Specifies attributes on the cookie that dbAuth sets in order to remember
185
+ // who is logged in. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies
186
+ cookie: {
187
+ attributes: {
188
+ HttpOnly: true,
189
+ Path: '/',
190
+ SameSite: 'Lax',
191
+ Secure: process.env.NODE_ENV !== 'development',
192
+
193
+ // If you need to allow other domains (besides the api side) access to
194
+ // the dbAuth session cookie:
195
+ // Domain: 'example.com',
196
+ },
197
+ name: cookieName,
198
+ },
199
+
200
+ forgotPassword: forgotPasswordOptions,
201
+ login: loginOptions,
202
+ resetPassword: resetPasswordOptions,
203
+ signup: signupOptions,
204
+ })
205
+
206
+ return await authHandler.invoke()
207
+ }
@@ -0,0 +1,225 @@
1
+ import type { APIGatewayProxyEvent, Context } from 'aws-lambda'
2
+
3
+ import { DbAuthHandler } from '@cedarjs/auth-dbauth-api'
4
+ import type { DbAuthHandlerOptions, UserType } from '@cedarjs/auth-dbauth-api'
5
+
6
+ import { cookieName } from 'src/lib/auth'
7
+ import { db } from 'src/lib/db'
8
+
9
+ export const handler = async (
10
+ event: APIGatewayProxyEvent,
11
+ context: Context
12
+ ) => {
13
+ const forgotPasswordOptions: DbAuthHandlerOptions['forgotPassword'] = {
14
+ // handler() is invoked after verifying that a user was found with the given
15
+ // username. This is where you can send the user an email with a link to
16
+ // reset their password. With the default dbAuth routes and field names, the
17
+ // URL to reset the password will be:
18
+ //
19
+ // https://example.com/reset-password?resetToken=${user.resetToken}
20
+ //
21
+ // Whatever is returned from this function will be returned from
22
+ // the `forgotPassword()` function that is destructured from `useAuth()`
23
+ // You could use this return value to, for example, show the email
24
+ // address in a toast message so the user will know it worked and where
25
+ // to look for the email.
26
+ handler: (user) => {
27
+ return user
28
+ },
29
+
30
+ // How long the resetToken is valid for, in seconds (default is 24 hours)
31
+ expires: 60 * 60 * 24,
32
+
33
+ errors: {
34
+ // for security reasons you may want to be vague here rather than expose
35
+ // the fact that the email address wasn't found (prevents fishing for
36
+ // valid email addresses)
37
+ usernameNotFound: 'Username not found',
38
+ // if the user somehow gets around client validation
39
+ usernameRequired: 'Username is required',
40
+ },
41
+ }
42
+
43
+ const loginOptions: DbAuthHandlerOptions['login'] = {
44
+ // handler() is called after finding the user that matches the
45
+ // username/password provided at login, but before actually considering them
46
+ // logged in. The `user` argument will be the user in the database that
47
+ // matched the username/password.
48
+ //
49
+ // If you want to allow this user to log in simply return the user.
50
+ //
51
+ // If you want to prevent someone logging in for another reason (maybe they
52
+ // didn't validate their email yet), throw an error and it will be returned
53
+ // by the `logIn()` function from `useAuth()` in the form of:
54
+ // `{ message: 'Error message' }`
55
+ handler: (user) => {
56
+ return user
57
+ },
58
+
59
+ errors: {
60
+ usernameOrPasswordMissing: 'Both username and password are required',
61
+ usernameNotFound: 'Username ${username} not found',
62
+ // For security reasons you may want to make this the same as the
63
+ // usernameNotFound error so that a malicious user can't use the error
64
+ // to narrow down if it's the username or password that's incorrect
65
+ incorrectPassword: 'Incorrect password for ${username}',
66
+ },
67
+
68
+ // How long a user will remain logged in, in seconds
69
+ expires: 60 * 60 * 24 * 365 * 10,
70
+ }
71
+
72
+ const resetPasswordOptions: DbAuthHandlerOptions['resetPassword'] = {
73
+ // handler() is invoked after the password has been successfully updated in
74
+ // the database. Returning anything truthy will automatically logs the user
75
+ // in. Return `false` otherwise, and in the Reset Password page redirect the
76
+ // user to the login page.
77
+ handler: (_user) => {
78
+ return true
79
+ },
80
+
81
+ // If `false` then the new password MUST be different than the current one
82
+ allowReusedPassword: true,
83
+
84
+ errors: {
85
+ // the resetToken is valid, but expired
86
+ resetTokenExpired: 'resetToken is expired',
87
+ // no user was found with the given resetToken
88
+ resetTokenInvalid: 'resetToken is invalid',
89
+ // the resetToken was not present in the URL
90
+ resetTokenRequired: 'resetToken is required',
91
+ // new password is the same as the old password (apparently they did not forget it)
92
+ reusedPassword: 'Must choose a new password',
93
+ },
94
+ }
95
+
96
+ interface UserAttributes {
97
+ name: string
98
+ }
99
+
100
+ const signupOptions: DbAuthHandlerOptions<
101
+ UserType,
102
+ UserAttributes
103
+ >['signup'] = {
104
+ // Whatever you want to happen to your data on new user signup. Redwood will
105
+ // check for duplicate usernames before calling this handler. At a minimum
106
+ // you need to save the `username`, `hashedPassword` and `salt` to your
107
+ // user table. `userAttributes` contains any additional object members that
108
+ // were included in the object given to the `signUp()` function you got
109
+ // from `useAuth()`.
110
+ //
111
+ // If you want the user to be immediately logged in, return the user that
112
+ // was created.
113
+ //
114
+ // If this handler throws an error, it will be returned by the `signUp()`
115
+ // function in the form of: `{ error: 'Error message' }`.
116
+ //
117
+ // If this returns anything else, it will be returned by the
118
+ // `signUp()` function in the form of: `{ message: 'String here' }`.
119
+ handler: ({
120
+ username,
121
+ hashedPassword,
122
+ salt,
123
+ userAttributes: _userAttributes,
124
+ }) => {
125
+ return db.user.create({
126
+ data: {
127
+ email: username,
128
+ hashedPassword: hashedPassword,
129
+ salt: salt,
130
+ // name: userAttributes.name
131
+ },
132
+ })
133
+ },
134
+
135
+ // Include any format checks for password here. Return `true` if the
136
+ // password is valid, otherwise throw a `PasswordValidationError`.
137
+ // Import the error along with `DbAuthHandler` from `@cedarjs/api` above.
138
+ passwordValidation: (_password) => {
139
+ return true
140
+ },
141
+
142
+ errors: {
143
+ // `field` will be either "username" or "password"
144
+ fieldMissing: '${field} is required',
145
+ usernameTaken: 'Username `${username}` already in use',
146
+ },
147
+ }
148
+
149
+ const authHandler = new DbAuthHandler(event, context, {
150
+ // Provide prisma db client
151
+ db: db,
152
+
153
+ // The name of the property you'd call on `db` to access your user table.
154
+ // ie. if your Prisma model is named `User` this value would be `user`, as in `db.user`
155
+ authModelAccessor: 'user',
156
+
157
+ // The name of the property you'd call on `db` to access your user credentials table.
158
+ // ie. if your Prisma model is named `UserCredential` this value would be `userCredential`, as in `db.userCredential`
159
+ credentialModelAccessor: 'userCredential',
160
+
161
+ // A map of what dbAuth calls a field to what your database calls it.
162
+ // `id` is whatever column you use to uniquely identify a user (probably
163
+ // something like `id` or `userId` or even `email`)
164
+ authFields: {
165
+ id: 'id',
166
+ username: 'email',
167
+ hashedPassword: 'hashedPassword',
168
+ salt: 'salt',
169
+ resetToken: 'resetToken',
170
+ resetTokenExpiresAt: 'resetTokenExpiresAt',
171
+ challenge: 'webAuthnChallenge',
172
+ },
173
+
174
+ // Specifies attributes on the cookie that dbAuth sets in order to remember
175
+ // who is logged in. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies
176
+ cookie: {
177
+ attributes: {
178
+ HttpOnly: true,
179
+ Path: '/',
180
+ SameSite: 'Lax',
181
+ Secure: process.env.NODE_ENV !== 'development' ? true : false,
182
+
183
+ // If you need to allow other domains (besides the api side) access to
184
+ // the dbAuth session cookie:
185
+ // Domain: 'example.com',
186
+ },
187
+ name: cookieName,
188
+ },
189
+
190
+ forgotPassword: forgotPasswordOptions,
191
+ login: loginOptions,
192
+ resetPassword: resetPasswordOptions,
193
+ signup: signupOptions,
194
+
195
+ // See https://redwoodjs.com/docs/auth/dbauth#webauthn for options
196
+ webAuthn: {
197
+ enabled: true,
198
+ // How long to allow re-auth via WebAuthn in seconds (default is 10 years).
199
+ // The `login.expires` time denotes how many seconds before a user will be
200
+ // logged out, and this value is how long they'll be to continue to use a
201
+ // fingerprint/face scan to log in again. When this one expires they
202
+ // *must* re-enter username and password to authenticate (WebAuthn will
203
+ // then be re-enabled for this amount of time).
204
+ expires: 60 * 60 * 24 * 365 * 10,
205
+ name: 'Redwood Application',
206
+ domain:
207
+ process.env.NODE_ENV === 'development' ? 'localhost' : 'server.com',
208
+ origin:
209
+ process.env.NODE_ENV === 'development'
210
+ ? 'http://localhost:8910'
211
+ : 'https://server.com',
212
+ type: 'platform',
213
+ timeout: 60000,
214
+ credentialFields: {
215
+ id: 'id',
216
+ userId: 'userId',
217
+ publicKey: 'publicKey',
218
+ transports: 'transports',
219
+ counter: 'counter',
220
+ },
221
+ },
222
+ })
223
+
224
+ return await authHandler.invoke()
225
+ }
@@ -0,0 +1,121 @@
1
+ import type { Decoded } from '@cedarjs/api'
2
+ import { AuthenticationError, ForbiddenError } from '@cedarjs/graphql-server'
3
+
4
+ import { db } from './db'
5
+
6
+ /**
7
+ * The name of the cookie that dbAuth sets
8
+ *
9
+ * %port% will be replaced with the port the api server is running on.
10
+ * If you have multiple RW apps running on the same host, you'll need to
11
+ * make sure they all use unique cookie names
12
+ */
13
+ export const cookieName = 'session_%port%'
14
+
15
+ /**
16
+ * The session object sent in as the first argument to getCurrentUser() will
17
+ * have a single key `id` containing the unique ID of the logged in user
18
+ * (whatever field you set as `authFields.id` in your auth function config).
19
+ * You'll need to update the call to `db` below if you use a different model
20
+ * name or unique field name, for example:
21
+ *
22
+ * return await db.profile.findUnique({ where: { email: session.id } })
23
+ * ───┬─── ──┬──
24
+ * model accessor ─┘ unique id field name ─┘
25
+ *
26
+ * !! BEWARE !! Anything returned from this function will be available to the
27
+ * client--it becomes the content of `currentUser` on the web side (as well as
28
+ * `context.currentUser` on the api side). You should carefully add additional
29
+ * fields to the `select` object below once you've decided they are safe to be
30
+ * seen if someone were to open the Web Inspector in their browser.
31
+ */
32
+ export const getCurrentUser = async (session: Decoded) => {
33
+ if (!session || typeof session.id !== 'number') {
34
+ throw new Error('Invalid session')
35
+ }
36
+
37
+ return await db.user.findUnique({
38
+ where: { id: session.id },
39
+ select: { id: true },
40
+ })
41
+ }
42
+
43
+ /**
44
+ * The user is authenticated if there is a currentUser in the context
45
+ *
46
+ * @returns {boolean} - If the currentUser is authenticated
47
+ */
48
+ export const isAuthenticated = (): boolean => {
49
+ return !!context.currentUser
50
+ }
51
+
52
+ /**
53
+ * When checking role membership, roles can be a single value, a list, or none.
54
+ * You can use Prisma enums too (if you're using them for roles), just import your enum type from `@prisma/client`
55
+ */
56
+ type AllowedRoles = string | string[] | undefined
57
+
58
+ /**
59
+ * Checks if the currentUser is authenticated (and assigned one of the given roles)
60
+ *
61
+ * @param roles: {@link AllowedRoles} - Checks if the currentUser is assigned one of these roles
62
+ *
63
+ * @returns {boolean} - Returns true if the currentUser is logged in and assigned one of the given roles,
64
+ * or when no roles are provided to check against. Otherwise returns false.
65
+ */
66
+ export const hasRole = (roles: AllowedRoles): boolean => {
67
+ if (!isAuthenticated()) {
68
+ return false
69
+ }
70
+
71
+ const currentUserRoles = context.currentUser?.roles
72
+
73
+ if (typeof roles === 'string') {
74
+ if (typeof currentUserRoles === 'string') {
75
+ // roles to check is a string, currentUser.roles is a string
76
+ return currentUserRoles === roles
77
+ } else if (Array.isArray(currentUserRoles)) {
78
+ // roles to check is a string, currentUser.roles is an array
79
+ return currentUserRoles?.some((allowedRole) => roles === allowedRole)
80
+ }
81
+ }
82
+
83
+ if (Array.isArray(roles)) {
84
+ if (Array.isArray(currentUserRoles)) {
85
+ // roles to check is an array, currentUser.roles is an array
86
+ return currentUserRoles?.some((allowedRole) =>
87
+ roles.includes(allowedRole)
88
+ )
89
+ } else if (typeof currentUserRoles === 'string') {
90
+ // roles to check is an array, currentUser.roles is a string
91
+ return roles.some((allowedRole) => currentUserRoles === allowedRole)
92
+ }
93
+ }
94
+
95
+ // roles not found
96
+ return false
97
+ }
98
+
99
+ /**
100
+ * Use requireAuth in your services to check that a user is logged in,
101
+ * whether or not they are assigned a role, and optionally raise an
102
+ * error if they're not.
103
+ *
104
+ * @param roles: {@link AllowedRoles} - When checking role membership, these roles grant access.
105
+ *
106
+ * @returns - If the currentUser is authenticated (and assigned one of the given roles)
107
+ *
108
+ * @throws {@link AuthenticationError} - If the currentUser is not authenticated
109
+ * @throws {@link ForbiddenError} If the currentUser is not allowed due to role permissions
110
+ *
111
+ * @see https://github.com/cedarjs/cedar/tree/main/packages/auth for examples
112
+ */
113
+ export const requireAuth = ({ roles }: { roles?: AllowedRoles } = {}) => {
114
+ if (!isAuthenticated()) {
115
+ throw new AuthenticationError("You don't have permission to do that.")
116
+ }
117
+
118
+ if (roles && !hasRole(roles)) {
119
+ throw new ForbiddenError("You don't have access to do that.")
120
+ }
121
+ }
@@ -0,0 +1,7 @@
1
+ 'use client'
2
+
3
+ import { createDbAuthClient, createAuth } from '@cedarjs/auth-dbauth-web'
4
+
5
+ const dbAuthClient = createDbAuthClient()
6
+
7
+ export const { AuthProvider, useAuth } = createAuth(dbAuthClient)
@@ -0,0 +1,5 @@
1
+ import { createDbAuthClient, createAuth } from '@cedarjs/auth-dbauth-web'
2
+
3
+ const dbAuthClient = createDbAuthClient()
4
+
5
+ export const { AuthProvider, useAuth } = createAuth(dbAuthClient)
@@ -0,0 +1,8 @@
1
+ 'use client'
2
+
3
+ import { createDbAuthClient, createAuth } from '@cedarjs/auth-dbauth-web'
4
+ import WebAuthnClient from '@cedarjs/auth-dbauth-web/webAuthn'
5
+
6
+ const dbAuthClient = createDbAuthClient({ webAuthn: new WebAuthnClient() })
7
+
8
+ export const { AuthProvider, useAuth } = createAuth(dbAuthClient)
@@ -0,0 +1,6 @@
1
+ import { createDbAuthClient, createAuth } from '@cedarjs/auth-dbauth-web'
2
+ import WebAuthnClient from '@cedarjs/auth-dbauth-web/webAuthn'
3
+
4
+ const dbAuthClient = createDbAuthClient({ webAuthn: new WebAuthnClient() })
5
+
6
+ export const { AuthProvider, useAuth } = createAuth(dbAuthClient)
@@ -0,0 +1,11 @@
1
+ import type { AuthGeneratorCtx } from '@cedarjs/cli-helpers/src/auth/authTasks.js';
2
+ export { extraTask } from './setupData';
3
+ export declare const webPackages: string[];
4
+ export declare const apiPackages: string[];
5
+ export declare const createUserModelTask: {
6
+ title: string;
7
+ task: (ctx: AuthGeneratorCtx) => Promise<void>;
8
+ };
9
+ export declare const notes: string[];
10
+ export declare const noteGenerate: string[];
11
+ //# sourceMappingURL=webAuthn.setupData.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webAuthn.setupData.d.ts","sourceRoot":"","sources":["../src/webAuthn.setupData.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4CAA4C,CAAA;AAKlF,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAGvC,eAAO,MAAM,WAAW,UAAiC,CAAA;AAGzD,eAAO,MAAM,WAAW,UAAgC,CAAA;AAExD,eAAO,MAAM,mBAAmB;;gBAEZ,gBAAgB;CA+BnC,CAAA;AAGD,eAAO,MAAM,KAAK,UAoEjB,CAAA;AAED,eAAO,MAAM,YAAY,UAMxB,CAAA"}
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+
3
+ var _Object$defineProperty = require("@babel/runtime-corejs3/core-js/object/define-property");
4
+ var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault").default;
5
+ _Object$defineProperty(exports, "__esModule", {
6
+ value: true
7
+ });
8
+ exports.createUserModelTask = exports.apiPackages = void 0;
9
+ _Object$defineProperty(exports, "extraTask", {
10
+ enumerable: true,
11
+ get: function () {
12
+ return _setupData.extraTask;
13
+ }
14
+ });
15
+ exports.webPackages = exports.notes = exports.noteGenerate = void 0;
16
+ var _path = _interopRequireDefault(require("path"));
17
+ var _cliHelpers = require("@cedarjs/cli-helpers");
18
+ var _shared = require("./shared");
19
+ var _setupData = require("./setupData");
20
+ // copy some identical values from dbAuth provider
21
+
22
+ // required packages to install on the web side
23
+ const webPackages = exports.webPackages = ['@simplewebauthn/browser@^7'];
24
+
25
+ // required packages to install on the api side
26
+ const apiPackages = exports.apiPackages = ['@simplewebauthn/server@^7'];
27
+ const createUserModelTask = exports.createUserModelTask = {
28
+ title: 'Creating model `User`...',
29
+ task: async ctx => {
30
+ const hasUserModel = await (0, _shared.hasModel)('User');
31
+ if (hasUserModel && !ctx.force) {
32
+ throw new Error('User model already exists');
33
+ }
34
+ (0, _shared.addModels)(`
35
+ model User {
36
+ id Int @id @default(autoincrement())
37
+ email String @unique
38
+ hashedPassword String
39
+ salt String
40
+ resetToken String?
41
+ resetTokenExpiresAt DateTime?
42
+ webAuthnChallenge String? @unique
43
+ credentials UserCredential[]
44
+ createdAt DateTime @default(now())
45
+ updatedAt DateTime @updatedAt
46
+ }
47
+
48
+ model UserCredential {
49
+ id String @id
50
+ userId Int
51
+ user User @relation(fields: [userId], references: [id])
52
+ publicKey Bytes
53
+ transports String?
54
+ counter BigInt
55
+ }
56
+ `);
57
+ }
58
+ };
59
+
60
+ // any notes to print out when the job is done
61
+ const notes = exports.notes = [`${_cliHelpers.colors.warning('Done! But you have a little more work to do:')}\n`, 'You will need to add a couple of fields to your User table in order', 'to store a hashed password, salt, reset token, and to connect it to', 'a new UserCredential model to keep track of any devices used with', 'WebAuthn authentication:', '', ' model User {', ' id Int @id @default(autoincrement())', ' email String @unique', ' hashedPassword String', ' salt String', ' resetToken String?', ' resetTokenExpiresAt DateTime?', ' webAuthnChallenge String? @unique', ' credentials UserCredential[]', ' }', '', ' model UserCredential {', ' id String @id', ' userId Int', ' user User @relation(fields: [userId], references: [id])', ' publicKey Bytes', ' transports String?', ' counter BigInt', ' }', '', 'If you already have existing user records you will need to provide', 'a default value for `hashedPassword` and `salt` or Prisma complains, so', 'change those to: ', '', ' hashedPassword String @default("")', ' salt String @default("")', '', 'If you expose any of your user data via GraphQL be sure to exclude', '`hashedPassword` and `salt` (or whatever you named them) from the', 'SDL file that defines the fields for your user.', '', "You'll need to let Redwood know what fields you're using for your", "users' `id` and `username` fields. In this case we're using `id` and", '`email`, so update those in the `authFields` config in', `\`${_shared.functionsPath}/auth.js\`. This is also the place to tell Redwood if`, 'you used a different name for the `hashedPassword`, `salt`,', '`resetToken` or `resetTokenExpiresAt`, fields:`', '', ' authFields: {', " id: 'id',", " username: 'email',", " hashedPassword: 'hashedPassword',", " salt: 'salt',", " resetToken: 'resetToken',", " resetTokenExpiresAt: 'resetTokenExpiresAt',", " challenge: 'webAuthnChallenge'", ' },', '', "To get the actual user that's logged in, take a look at `getCurrentUser()`", `in \`${_shared.libPath}/auth.js\`. We default it to something simple, but you may`, 'use different names for your model or unique ID fields, in which case you', 'need to update those calls (instructions are in the comment above the code).', '', 'Finally, we created a SESSION_SECRET environment variable for you in', `${_path.default.join((0, _cliHelpers.getPaths)().base, '.env')}. This value should NOT be checked`, 'into version control and should be unique for each environment you', 'deploy to. If you ever need to log everyone out of your app at once', 'change this secret to a new value and deploy. To create a new secret, run:', '', ' yarn rw generate secret', ''];
62
+ const noteGenerate = exports.noteGenerate = ['', 'Need simple Login, Signup, Forgot Password pages and WebAuthn prompts?', "We've got a generator for those as well:", '', ' yarn rw generate dbAuth'];