@strav/http 0.2.13 → 0.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@strav/http",
3
- "version": "0.2.13",
3
+ "version": "0.3.1",
4
4
  "type": "module",
5
5
  "description": "HTTP layer for the Strav framework — router, server, middleware, authentication, sessions, validation, and views",
6
6
  "license": "MIT",
@@ -32,12 +32,14 @@
32
32
  "./policy/*": "./src/policy/*.ts",
33
33
  "./auth": "./src/auth/index.ts",
34
34
  "./auth/*": "./src/auth/*.ts",
35
+ "./auth/bridge": "./src/auth/bridge.ts",
35
36
  "./providers": "./src/providers/index.ts",
36
37
  "./providers/*": "./src/providers/*.ts"
37
38
  },
38
39
  "peerDependencies": {
39
- "@strav/kernel": "0.2.13",
40
- "@strav/database": "0.2.13"
40
+ "@strav/kernel": "0.3.1",
41
+ "@strav/database": "0.3.1",
42
+ "@strav/auth": "0.3.1"
41
43
  },
42
44
  "dependencies": {
43
45
  "@vue/compiler-sfc": "^3.5.28",
package/src/auth/auth.ts CHANGED
@@ -7,6 +7,23 @@ import Database from '@strav/database/database/database'
7
7
  export { extractUserId } from '@strav/database/helpers/identity'
8
8
  export { randomHex } from '@strav/kernel/helpers/crypto'
9
9
 
10
+ // Re-export commonly used auth primitives for convenience
11
+ export {
12
+ signJWT,
13
+ verifyJWT,
14
+ createAccessToken as createJWTAccessToken,
15
+ createRefreshToken as createJWTRefreshToken,
16
+ verifyAccessToken,
17
+ verifyRefreshToken
18
+ } from '@strav/auth/jwt'
19
+
20
+ export {
21
+ createSignedToken,
22
+ verifySignedToken,
23
+ createMagicLinkToken,
24
+ verifyMagicLinkToken
25
+ } from '@strav/auth/tokens'
26
+
10
27
  export interface TokenConfig {
11
28
  expiration: number | null
12
29
  }
@@ -0,0 +1,154 @@
1
+ import type Context from '../http/context.ts'
2
+ import { signJWT, verifyJWT, createMagicLinkToken, verifySignedToken } from '@strav/auth'
3
+ import type { JWTPayload, JWTSignOptions } from '@strav/auth'
4
+
5
+ /**
6
+ * Helper functions to bridge @strav/auth primitives with HTTP contexts.
7
+ * These utilities make it easy to use low-level auth tokens in HTTP scenarios.
8
+ */
9
+
10
+ /**
11
+ * Create a JWT token and return a Set-Cookie header value.
12
+ *
13
+ * @param payload - JWT payload
14
+ * @param secret - Secret for signing
15
+ * @param options - JWT and cookie options
16
+ * @returns Object containing the JWT token and Set-Cookie header value
17
+ */
18
+ export async function createJWTCookie(
19
+ payload: JWTPayload,
20
+ secret: string | Uint8Array,
21
+ options: {
22
+ cookieName?: string
23
+ cookieOptions?: {
24
+ domain?: string
25
+ path?: string
26
+ secure?: boolean
27
+ sameSite?: 'strict' | 'lax' | 'none'
28
+ maxAge?: number
29
+ }
30
+ } & JWTSignOptions = {}
31
+ ): Promise<{ token: string; cookieHeader: string }> {
32
+ const { cookieName = 'auth-token', cookieOptions = {}, ...jwtOptions } = options
33
+
34
+ const token = await signJWT(payload, secret, jwtOptions)
35
+
36
+ const cookieParts = [
37
+ `${cookieName}=${token}`,
38
+ 'HttpOnly',
39
+ 'Secure',
40
+ 'SameSite=Strict'
41
+ ]
42
+
43
+ if (cookieOptions.domain) cookieParts.push(`Domain=${cookieOptions.domain}`)
44
+ if (cookieOptions.path) cookieParts.push(`Path=${cookieOptions.path}`)
45
+ if (cookieOptions.maxAge) cookieParts.push(`Max-Age=${cookieOptions.maxAge}`)
46
+ if (cookieOptions.secure === false) cookieParts.splice(cookieParts.indexOf('Secure'), 1)
47
+ if (cookieOptions.sameSite) {
48
+ const index = cookieParts.findIndex(p => p.startsWith('SameSite'))
49
+ cookieParts[index] = `SameSite=${cookieOptions.sameSite}`
50
+ }
51
+
52
+ return {
53
+ token,
54
+ cookieHeader: cookieParts.join('; ')
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Extract and verify a JWT token from an HTTP-only cookie.
60
+ *
61
+ * @param ctx - The HTTP context
62
+ * @param secret - Secret for verification
63
+ * @param cookieName - Name of the cookie (defaults to 'auth-token')
64
+ * @returns The verified JWT payload or null if invalid
65
+ */
66
+ export async function verifyJWTCookie<T = JWTPayload>(
67
+ ctx: Context,
68
+ secret: string | Uint8Array,
69
+ cookieName: string = 'auth-token'
70
+ ): Promise<T | null> {
71
+ const token = ctx.cookie(cookieName)
72
+ if (!token) return null
73
+
74
+ try {
75
+ return await verifyJWT(token, secret) as T
76
+ } catch {
77
+ return null
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Create a magic link URL with an embedded signed token.
83
+ *
84
+ * @param baseUrl - Base URL for the magic link
85
+ * @param userId - User identifier
86
+ * @param options - Magic link options
87
+ * @returns Complete magic link URL
88
+ */
89
+ export function createMagicLinkURL(
90
+ baseUrl: string,
91
+ userId: string | number,
92
+ options: {
93
+ email?: string
94
+ redirect?: string
95
+ expiresInMinutes?: number
96
+ tokenParam?: string
97
+ } = {}
98
+ ): string {
99
+ const { tokenParam = 'token', ...tokenOptions } = options
100
+
101
+ const token = createMagicLinkToken(userId, tokenOptions)
102
+ const url = new URL(baseUrl)
103
+ url.searchParams.set(tokenParam, token)
104
+
105
+ if (options.redirect) {
106
+ url.searchParams.set('redirect', options.redirect)
107
+ }
108
+
109
+ return url.toString()
110
+ }
111
+
112
+ /**
113
+ * Extract and verify a magic link token from HTTP context.
114
+ *
115
+ * @param ctx - The HTTP context
116
+ * @param tokenParam - URL parameter containing the token (defaults to 'token')
117
+ * @returns The verified token payload or null if invalid
118
+ */
119
+ export function verifyMagicLinkFromContext(
120
+ ctx: Context,
121
+ tokenParam: string = 'token'
122
+ ): { sub: string | number; email?: string; redirect?: string } | null {
123
+ const token = ctx.query.get(tokenParam)
124
+ if (!token) return null
125
+
126
+ try {
127
+ return verifySignedToken(token)
128
+ } catch {
129
+ return null
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Extract Bearer token from Authorization header.
135
+ *
136
+ * @param ctx - The HTTP context
137
+ * @returns The token string or null if not present
138
+ */
139
+ export function extractBearerToken(ctx: Context): string | null {
140
+ const header = ctx.header('authorization')
141
+ if (!header?.startsWith('Bearer ')) return null
142
+
143
+ return header.slice(7)
144
+ }
145
+
146
+ /**
147
+ * Create an Authorization Bearer token header value.
148
+ *
149
+ * @param token - The token to set
150
+ * @returns The formatted Authorization header value
151
+ */
152
+ export function createBearerTokenHeader(token: string): string {
153
+ return `Bearer ${token}`
154
+ }
package/src/auth/index.ts CHANGED
@@ -5,3 +5,13 @@ export { csrf } from './middleware/csrf.ts'
5
5
  export { guest } from './middleware/guest.ts'
6
6
  export type { AuthConfig, TokenConfig } from './auth.ts'
7
7
  export type { AccessTokenData } from './access_token.ts'
8
+
9
+ // HTTP-specific auth bridge helpers
10
+ export * from './bridge.ts'
11
+
12
+ // Re-export low-level auth primitives from @strav/auth
13
+ export * from '@strav/auth/jwt'
14
+ export * from '@strav/auth/tokens'
15
+ export * from '@strav/auth/totp'
16
+ export * from '@strav/auth/oauth'
17
+ export * from '@strav/auth/validation'