also looking at this
Close unauthenticated /api/requests CRUD, and make the session cookie HttpOnly
#77
11 files
+552
-57
| @@ -0,0 +1,70 @@ | ||
| 1 | import type { RequestInstance } from '@stacksjs/types' | |
| 2 | import { Action } from '@stacksjs/actions' | |
| 3 | import { Auth, register } from '@stacksjs/auth' | |
| 4 | import { dispatch } from '@stacksjs/events' | |
| 5 | import { response } from '@stacksjs/router' | |
| 6 | import { schema } from '@stacksjs/validation' | |
| 7 | import { withSessionCookie } from '../../Support/sessionCookie' | |
| 8 | ||
| 9 | /** | |
| 10 | * Register — app-local override of the framework default, identical except that | |
| 11 | * the new session arrives as an `HttpOnly` cookie rather than being written by | |
| 12 | * the browser. See `app/Support/sessionCookie.ts`. | |
| 13 | * | |
| 14 | * Registration logs the user straight in, so it mints a session and therefore | |
| 15 | * needs the same cookie treatment as login; without it a freshly registered | |
| 16 | * user would be the one remaining path that persisted a script-readable token. | |
| 17 | */ | |
| 18 | export default new Action({ | |
| 19 | name: 'RegisterAction', | |
| 20 | description: 'Register a new user', | |
| 21 | method: 'POST', | |
| 22 | ||
| 23 | validations: { | |
| 24 | email: { | |
| 25 | rule: schema.string().email(), | |
| 26 | message: 'Email must be a valid email address.', | |
| 27 | }, | |
| 28 | password: { | |
| 29 | rule: schema.string().min(6).max(255), | |
| 30 | message: 'Password must be between 6 and 255 characters.', | |
| 31 | }, | |
| 32 | name: { | |
| 33 | rule: schema.string().min(2).max(255), | |
| 34 | message: 'Name must be between 2 and 255 characters.', | |
| 35 | }, | |
| 36 | }, | |
| 37 | ||
| 38 | async handle(request: RequestInstance) { | |
| 39 | const email = request.get('email') | |
| 40 | const password = request.get('password') | |
| 41 | const name = request.get('name') | |
| 42 | ||
| 43 | const result = await register({ email, password, name }) | |
| 44 | ||
| 45 | if (result) { | |
| 46 | const user = await Auth.getUserFromToken(result.token) | |
| 47 | ||
| 48 | // Fire `user:registered` so app/Events.ts listeners (welcome email, CRM | |
| 49 | // sync, etc.) run. Fire-and-forget — listener errors are caught by the | |
| 50 | // wildcard handler so a flaky welcome email doesn't fail registration. | |
| 51 | dispatch('user:registered', { | |
| 52 | id: user?.id, | |
| 53 | email: user?.email, | |
| 54 | name: user?.name, | |
| 55 | to: user?.email, | |
| 56 | }) | |
| 57 | ||
| 58 | return withSessionCookie({ | |
| 59 | token: result.token, | |
| 60 | user: { | |
| 61 | id: user?.id, | |
| 62 | email: user?.email, | |
| 63 | name: user?.name, | |
| 64 | }, | |
| 65 | }, result.token) | |
| 66 | } | |
| 67 | ||
| 68 | return response.error('Registration failed') | |
| 69 | }, | |
| 70 | }) | |