-
-
Notifications
You must be signed in to change notification settings - Fork 341
Expand file tree
/
Copy pathauth.server.ts
More file actions
118 lines (105 loc) · 3.22 KB
/
auth.server.ts
File metadata and controls
118 lines (105 loc) · 3.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
/**
* Auth Server Utilities
*
* This module delegates to the isolated auth module at ~/auth/
* for backward compatibility with existing imports.
*
* For new code, import directly from '~/auth/index.server'.
*/
import { createServerFn } from '@tanstack/react-start'
import { getRequest } from '@tanstack/react-start/server'
import { getAuthService, getAuthGuards } from '~/auth/index.server'
import type { Capability } from '~/auth/index.server'
import { ADMIN_ACCESS_CAPABILITIES } from '~/db/types'
/**
* Server function to get the current user.
*/
export const getCurrentUser = createServerFn({ method: 'POST' }).handler(
async () => {
const request = getRequest()
const authService = getAuthService()
return authService.getCurrentUser(request)
},
)
/**
* Server function to require authentication.
* Called from route beforeLoad guards.
* Do NOT use this inside server function handlers -- use guards directly.
*/
export const requireAuth = createServerFn({ method: 'POST' }).handler(
async () => {
const request = getRequest()
const guards = getAuthGuards()
return guards.requireAuth(request)
},
)
/**
* Server function to require a specific capability.
* Called from route beforeLoad guards.
* Do NOT use this inside server function handlers -- use guards directly.
*/
export const requireCapability = createServerFn({ method: 'POST' })
.inputValidator((data: { capability: string }) => ({
capability: data.capability as Capability,
}))
.handler(async ({ data: { capability } }) => {
const request = getRequest()
const guards = getAuthGuards()
return guards.requireCapability(request, capability)
})
/**
* Load user from session (non-blocking, returns null if not authenticated)
* Can be called from loaders or beforeLoad
*/
export async function loadUser() {
try {
return await getCurrentUser()
} catch {
return null
}
}
/**
* Require authentication (throws if not authenticated)
* Can be called from loaders or beforeLoad
*/
export async function requireAuthUser() {
try {
return await requireAuth()
} catch {
throw new Error('Not authenticated')
}
}
/**
* Require a specific capability (throws if not authorized)
* Can be called from loaders or beforeLoad
*/
export async function requireCapabilityUser(capability: string) {
try {
return await requireCapability({ data: { capability } })
} catch {
throw new Error(`Missing required capability: ${capability}`)
}
}
/** @deprecated Import from ~/db/types instead */
export { ADMIN_ACCESS_CAPABILITIES as ADMIN_CAPABILITIES } from '~/db/types'
/**
* Server function to require any admin-like capability.
* Used for accessing the /admin area (each sub-route checks specific capabilities).
*/
export const requireAnyAdminCapability = createServerFn({
method: 'POST',
}).handler(async () => {
const request = getRequest()
const authService = getAuthService()
const user = await authService.getCurrentUser(request)
if (!user) {
throw new Error('Not authenticated')
}
const hasAdminCapability = user.capabilities.some((cap) =>
(ADMIN_ACCESS_CAPABILITIES as readonly string[]).includes(cap),
)
if (!hasAdminCapability) {
throw new Error('Admin access required')
}
return user
})