@remix-run/session-middleware 0.3.4 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -7
- package/dist/index.js +1 -1
- package/dist/lib/session.d.ts.map +1 -1
- package/dist/lib/session.js +6 -1
- package/package.json +13 -13
- package/src/lib/session.ts +12 -1
package/README.md
CHANGED
|
@@ -24,7 +24,6 @@ import { session } from 'remix/middleware/session'
|
|
|
24
24
|
|
|
25
25
|
let sessionCookie = createCookie('__session', {
|
|
26
26
|
secrets: ['s3cr3t'], // session cookies must be signed!
|
|
27
|
-
httpOnly: true,
|
|
28
27
|
secure: true,
|
|
29
28
|
sameSite: 'lax',
|
|
30
29
|
})
|
|
@@ -50,21 +49,24 @@ The middleware:
|
|
|
50
49
|
Use `context.session` (or `context.get(Session)`) for normal session reads and writes.
|
|
51
50
|
|
|
52
51
|
Note: The session cookie must be signed for security. This prevents tampering with the session data on the client.
|
|
52
|
+
Session cookies are HTTP-only by default.
|
|
53
53
|
|
|
54
54
|
### Login/Logout Flow
|
|
55
55
|
|
|
56
56
|
A basic login/logout flow could look like this:
|
|
57
57
|
|
|
58
58
|
```ts
|
|
59
|
-
import
|
|
59
|
+
import { html } from 'remix/html-template'
|
|
60
|
+
import { createHtmlResponse } from 'remix/response/html'
|
|
61
|
+
import { redirect } from 'remix/response/redirect'
|
|
60
62
|
|
|
61
63
|
router.get('/login', ({ session }) => {
|
|
62
64
|
let error = session.get('error')
|
|
63
|
-
return
|
|
65
|
+
return createHtmlResponse(html`
|
|
64
66
|
<html>
|
|
65
67
|
<body>
|
|
66
68
|
<h1>Login</h1>
|
|
67
|
-
${typeof error === 'string' ?
|
|
69
|
+
${typeof error === 'string' ? html`<div class="error">${error}</div>` : null}
|
|
68
70
|
<form method="POST" action="/login">
|
|
69
71
|
<input type="text" name="username" placeholder="Username" />
|
|
70
72
|
<input type="password" name="password" placeholder="Password" />
|
|
@@ -83,18 +85,18 @@ router.post('/login', ({ get, session }) => {
|
|
|
83
85
|
let user = authenticateUser(username, password)
|
|
84
86
|
if (!user) {
|
|
85
87
|
session.flash('error', 'Invalid username or password')
|
|
86
|
-
return
|
|
88
|
+
return redirect('/login')
|
|
87
89
|
}
|
|
88
90
|
|
|
89
91
|
session.regenerateId()
|
|
90
92
|
session.set('userId', user.id)
|
|
91
93
|
|
|
92
|
-
return
|
|
94
|
+
return redirect('/dashboard')
|
|
93
95
|
})
|
|
94
96
|
|
|
95
97
|
router.post('/logout', ({ session }) => {
|
|
96
98
|
session.destroy()
|
|
97
|
-
return
|
|
99
|
+
return redirect('/')
|
|
98
100
|
})
|
|
99
101
|
```
|
|
100
102
|
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { session } from
|
|
1
|
+
export { session } from './lib/session.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/lib/session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AACzD,OAAO,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAEjE;;;;;;GAMG;AACH,wBAAgB,OAAO,CACrB,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,cAAc,GAC7B,UAAU,CAAC;IAAE,GAAG,EAAE,OAAO,OAAO,CAAC;IAAC,KAAK,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,SAAS,CAAA;CAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/lib/session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AACzD,OAAO,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAEjE;;;;;;GAMG;AACH,wBAAgB,OAAO,CACrB,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,cAAc,GAC7B,UAAU,CAAC;IAAE,GAAG,EAAE,OAAO,OAAO,CAAC;IAAC,KAAK,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,SAAS,CAAA;CAAE,CAAC,CAyC1E"}
|
package/dist/lib/session.js
CHANGED
|
@@ -10,6 +10,9 @@ export function session(sessionCookie, sessionStorage) {
|
|
|
10
10
|
if (!sessionCookie.signed) {
|
|
11
11
|
throw new Error('Session cookie must be signed');
|
|
12
12
|
}
|
|
13
|
+
if (sessionCookie.httpOnly === false) {
|
|
14
|
+
console.warn(`Session cookie "${sessionCookie.name}" is configured with httpOnly: false and may be accessible to client-side JavaScript.`);
|
|
15
|
+
}
|
|
13
16
|
return async (context, next) => {
|
|
14
17
|
if (context.has(Session)) {
|
|
15
18
|
throw new Error('Existing session found, refusing to overwrite');
|
|
@@ -25,7 +28,9 @@ export function session(sessionCookie, sessionStorage) {
|
|
|
25
28
|
if (setCookieValue != null) {
|
|
26
29
|
// make sure the response is mutable
|
|
27
30
|
response = new Response(response.body, response);
|
|
28
|
-
response.headers.append('Set-Cookie', await sessionCookie.serialize(setCookieValue
|
|
31
|
+
response.headers.append('Set-Cookie', await sessionCookie.serialize(setCookieValue, {
|
|
32
|
+
httpOnly: sessionCookie.httpOnly ?? true,
|
|
33
|
+
}));
|
|
29
34
|
}
|
|
30
35
|
return response;
|
|
31
36
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remix-run/session-middleware",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Middleware for managing sessions with cookie-based storage",
|
|
5
5
|
"author": "Michael Jackson <mjijackson@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -27,18 +27,18 @@
|
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@types/node": "^24.6.0",
|
|
30
|
-
"
|
|
30
|
+
"typescript": "^7.0.2",
|
|
31
|
+
"@remix-run/cookie": "0.6.0",
|
|
31
32
|
"@remix-run/assert": "0.3.0",
|
|
32
|
-
"@remix-run/
|
|
33
|
+
"@remix-run/fetch-router": "0.21.0",
|
|
34
|
+
"@remix-run/session": "0.4.2",
|
|
33
35
|
"@remix-run/headers": "0.21.1",
|
|
34
|
-
"@remix-run/
|
|
35
|
-
"@remix-run/test": "0.5.0",
|
|
36
|
-
"@remix-run/session": "0.4.2"
|
|
36
|
+
"@remix-run/test": "0.6.0"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@remix-run/
|
|
40
|
-
"@remix-run/fetch-router": "^0.
|
|
41
|
-
"@remix-run/
|
|
39
|
+
"@remix-run/cookie": "^0.6.0",
|
|
40
|
+
"@remix-run/fetch-router": "^0.21.0",
|
|
41
|
+
"@remix-run/session": "^0.4.2"
|
|
42
42
|
},
|
|
43
43
|
"keywords": [
|
|
44
44
|
"fetch",
|
|
@@ -49,10 +49,10 @@
|
|
|
49
49
|
"session-management"
|
|
50
50
|
],
|
|
51
51
|
"scripts": {
|
|
52
|
-
"build": "
|
|
52
|
+
"build": "tsc -p tsconfig.build.json",
|
|
53
53
|
"clean": "git clean -fdX",
|
|
54
|
-
"test": "remix
|
|
55
|
-
"test:bun": "bun x --bun remix
|
|
56
|
-
"typecheck": "
|
|
54
|
+
"test": "remix test",
|
|
55
|
+
"test:bun": "bun x --bun remix test",
|
|
56
|
+
"typecheck": "tsc --noEmit"
|
|
57
57
|
}
|
|
58
58
|
}
|
package/src/lib/session.ts
CHANGED
|
@@ -17,6 +17,12 @@ export function session(
|
|
|
17
17
|
throw new Error('Session cookie must be signed')
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
if (sessionCookie.httpOnly === false) {
|
|
21
|
+
console.warn(
|
|
22
|
+
`Session cookie "${sessionCookie.name}" is configured with httpOnly: false and may be accessible to client-side JavaScript.`,
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
20
26
|
return async (context, next) => {
|
|
21
27
|
if (context.has(Session)) {
|
|
22
28
|
throw new Error('Existing session found, refusing to overwrite')
|
|
@@ -37,7 +43,12 @@ export function session(
|
|
|
37
43
|
if (setCookieValue != null) {
|
|
38
44
|
// make sure the response is mutable
|
|
39
45
|
response = new Response(response.body, response)
|
|
40
|
-
response.headers.append(
|
|
46
|
+
response.headers.append(
|
|
47
|
+
'Set-Cookie',
|
|
48
|
+
await sessionCookie.serialize(setCookieValue, {
|
|
49
|
+
httpOnly: sessionCookie.httpOnly ?? true,
|
|
50
|
+
}),
|
|
51
|
+
)
|
|
41
52
|
}
|
|
42
53
|
|
|
43
54
|
return response
|