-
Notifications
You must be signed in to change notification settings - Fork 0
/
controller.ts
115 lines (103 loc) · 3.33 KB
/
controller.ts
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
import { appLogger, type AppLogger } from '../helpers/logger'
import type { ControllerOutput } from '../_types'
import { AuthModel } from './model'
import { hasChallengeExpired } from './helpers/has-challenge-expired'
import { Rola } from '@common/rola'
import { SignedChallenge } from '@common/rdt'
import type { GatewayApiClient } from '@common/gateway-sdk'
import { CURRENT_NETWORK } from '@networks'
import { err, errAsync, okAsync } from 'neverthrow'
import { OAuth2 } from './oauth2'
import { UserModel } from '../user/model'
import type { Cookies } from '@sveltejs/kit'
import { safeParse } from 'valibot'
export type AuthController = ReturnType<typeof AuthController>
export const AuthController = ({
authModel = AuthModel(),
userModel = UserModel(),
oAuth2 = OAuth2(),
logger,
expectedOrigin = 'http://localhost:5173',
dAppDefinitionAddress = CURRENT_NETWORK.dashboardDappAddress,
networkId = CURRENT_NETWORK.id,
gatewayApiClient
}: Partial<{
authModel: AuthModel
userModel: UserModel
gatewayApiClient: GatewayApiClient
oAuth2: OAuth2
logger: AppLogger
expectedOrigin: string
dAppDefinitionAddress: string
networkId: number
}>) => {
const { verifySignedChallenge } = Rola({
applicationName: 'Radix Dashboard',
expectedOrigin,
dAppDefinitionAddress,
networkId,
gatewayApiClient
})
const createChallenge = (): ControllerOutput<{ challenge: string }> =>
authModel
.createChallenge()
.map((challenge) => ({ data: { challenge }, httpResponseCode: 201 }))
const login = (
signedChallenge: SignedChallenge,
cookies: Cookies
): ControllerOutput<{
authToken: string
headers: { ['Set-Cookie']: string }
}> => {
logger?.debug('Verifying signed challenge', signedChallenge)
if (!safeParse(SignedChallenge, signedChallenge))
return errAsync({
httpResponseCode: 400,
reason: 'invalidRequestBody'
})
return authModel
.getAndDelete(signedChallenge.challenge)
.andThen(hasChallengeExpired)
.andThen(() => verifySignedChallenge(signedChallenge))
.mapErr(({ reason, jsError }) => ({
httpResponseCode: 400,
reason,
jsError
}))
.andThen(() =>
userModel.create({ identityAddress: signedChallenge.address })
)
.andThen(() => oAuth2.createTokens(signedChallenge.address))
.map(({ authToken, refreshToken }) => ({
data: {
authToken,
headers: oAuth2.createRefreshTokenCookie(refreshToken, cookies)
},
httpResponseCode: 200
}))
}
const renewAuthToken = (cookies: Cookies) => oAuth2.renewAuthToken(cookies)
const verifyAuthToken = (authorizationHeaderValue: string | null) => {
const authToken = (authorizationHeaderValue || '').split(' ')[1]
return authToken
? oAuth2.verifyToken(authToken)
: err({ reason: 'invalidToken' })
}
const logout = (cookies: Cookies) => {
return oAuth2.logout(cookies)
}
return {
createChallenge,
login,
logout,
renewAuthToken,
isValid: verifyAuthToken
}
}
const options = {
expectedOrigin: process.env.URL || 'http://localhost:5173',
networkId: CURRENT_NETWORK.id,
dAppDefinitionAddress: CURRENT_NETWORK.dashboardDappAddress
}
appLogger.debug('authController', options)
export const authController = AuthController({ ...options, logger: appLogger })