-
Notifications
You must be signed in to change notification settings - Fork 143
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
chore: hydra POC #2707
chore: hydra POC #2707
Conversation
84b72ff
to
1a3b540
Compare
1a3b540
to
7340bbf
Compare
f82fd53
to
73add5e
Compare
7340bbf
to
cc16259
Compare
f06cb68
to
d082cf8
Compare
cc16259
to
11020ba
Compare
02d83b5
to
f59254b
Compare
158dee0
to
12d7fc3
Compare
720ff5f
to
866adfb
Compare
3c31d9e
to
697af28
Compare
747337c
to
7423e5d
Compare
/Users/n/Code/galoy/lib/servers/middlewares/session.js:22const scope = tokenPayload?.scope.split(" ");^TypeError: Cannot read properties of undefined (reading 'split') |
bcdb6f7
to
1f97547
Compare
332c624
to
9e6edb2
Compare
9e6edb2
to
b18c782
Compare
bbcde1c
to
4f55f26
Compare
#3288 |
4f55f26
to
0fb2130
Compare
chore: renaming folder chore: moving some of the change to the rest layer instead of graphql chore: some iteration on hydra feat: adding scope middleware chore: testing client credentials chore: adding appId to know whether a request comes from hydra or kratos chore: update docs fix: test?
0fb2130
to
b84468d
Compare
authRouter.post("/login", async (req: Request, res: Response) => { | ||
const ip = req.originalIp | ||
const code = req.body.authCode | ||
const phone = checkedToPhoneNumber(req.body.phoneNumber) | ||
if (phone instanceof Error) return res.status(400).send("invalid phone") | ||
const loginResp = await Authentication.loginWithPhoneCookie({ | ||
phone, | ||
code, | ||
ip, | ||
}) | ||
if (loginResp instanceof Error) { | ||
return res.status(500).send({ error: mapError(loginResp).message }) | ||
} | ||
|
||
try { | ||
const kratosCookies = parseKratosCookies(loginResp.cookiesToSendBackToClient) | ||
const kratosUserId: UserId | undefined = loginResp.kratosUserId | ||
const csrfCookie = kratosCookies.csrf() | ||
const kratosSessionCookie = kratosCookies.kratosSession() | ||
if (!csrfCookie || !kratosSessionCookie) { | ||
return res.status(500).send({ error: "Missing csrf or ory_kratos_session cookie" }) | ||
} | ||
const kratosCookieStr = kratosCookies.kratosSessionAsString() | ||
const result = await validateKratosCookie(kratosCookieStr) | ||
if (result instanceof Error) { | ||
return res.status(500).send({ error: result.message }) | ||
} | ||
res.cookie( | ||
kratosSessionCookie.name, | ||
kratosSessionCookie.value, | ||
kratosCookies.formatCookieOptions(kratosSessionCookie), | ||
) | ||
res.cookie( | ||
csrfCookie.name, | ||
csrfCookie.value, | ||
kratosCookies.formatCookieOptions(csrfCookie), | ||
) | ||
return res.send({ | ||
identity: { | ||
id: kratosUserId, | ||
uid: kratosUserId, | ||
phoneNumber: phone, | ||
}, | ||
}) | ||
} catch (error) { | ||
recordExceptionInCurrentSpan({ error }) | ||
return res.status(500).send({ result: "Error parsing cookies" }) | ||
} | ||
}) |
Check failure
Code scanning / CodeQL
Missing rate limiting High
authorization
authRouter.post("/create/device-account", async (req: Request, res: Response) => { | ||
const ip = req.originalIp | ||
const user = basicAuth(req) | ||
|
||
if (!user?.name || !user?.pass) { | ||
return res.status(422).send({ error: "Bad input" }) | ||
} | ||
|
||
const username = user.name | ||
const password = user.pass | ||
const deviceId = username | ||
|
||
try { | ||
const authToken = await Authentication.loginWithDevice({ | ||
username, | ||
password, | ||
ip, | ||
deviceId, | ||
}) | ||
if (authToken instanceof Error) { | ||
recordExceptionInCurrentSpan({ error: authToken }) | ||
return res.status(500).send({ error: authToken.message }) | ||
} | ||
addAttributesToCurrentSpan({ "login.deviceAccount": deviceId }) | ||
return res.status(200).send({ | ||
result: authToken, | ||
}) | ||
} catch (err) { | ||
recordExceptionInCurrentSpan({ error: err }) | ||
return res.status(500).send({ error: parseErrorMessageFromUnknown(err) }) | ||
} | ||
}) |
Check failure
Code scanning / CodeQL
Missing rate limiting High
authorization
This route handler performs
authorization
authRouter.post("/email/login", async (req: Request, res: Response) => { | ||
const ip = req.originalIp | ||
|
||
const emailLoginIdRaw = req.body.emailLoginId | ||
if (!emailLoginIdRaw) { | ||
return res.status(422).send({ error: "Missing input" }) | ||
} | ||
|
||
const emailLoginId = checkedToEmailLoginId(emailLoginIdRaw) | ||
if (emailLoginId instanceof Error) { | ||
return res.status(422).send({ error: emailLoginId.message }) | ||
} | ||
|
||
const codeRaw = req.body.code | ||
if (!codeRaw) { | ||
return res.status(422).send({ error: "Missing input" }) | ||
} | ||
|
||
const code = checkedToEmailCode(codeRaw) | ||
if (code instanceof Error) { | ||
return res.status(422).send({ error: code.message }) | ||
} | ||
|
||
try { | ||
const result = await loginWithEmailToken({ ip, emailFlowId: emailLoginId, code }) | ||
if (result instanceof EmailCodeInvalidError) { | ||
recordExceptionInCurrentSpan({ error: result }) | ||
return res.status(401).send({ error: "invalid code" }) | ||
} | ||
if ( | ||
result instanceof EmailValidationSubmittedTooOftenError || | ||
result instanceof UserLoginIpRateLimiterExceededError | ||
) { | ||
recordExceptionInCurrentSpan({ error: result }) | ||
return res.status(429).send({ error: "too many requests" }) | ||
} | ||
if (result instanceof Error) { | ||
recordExceptionInCurrentSpan({ error: result }) | ||
return res.status(500).send({ error: result.message }) | ||
} | ||
const { authToken, totpRequired, id } = result | ||
return res.status(200).send({ | ||
result: { authToken, totpRequired, id }, | ||
}) | ||
} catch (err) { | ||
recordExceptionInCurrentSpan({ error: err }) | ||
return res.status(500).send({ error: parseErrorMessageFromUnknown(err) }) | ||
} | ||
}) |
Check failure
Code scanning / CodeQL
Missing rate limiting High
authorization
This route handler performs
authorization
authRouter.post("/totp/validate", async (req: Request, res: Response) => { | ||
const totpCodeRaw = req.body.totpCode | ||
if (!totpCodeRaw) { | ||
return res.status(422).send({ error: "Missing input" }) | ||
} | ||
|
||
const totpCode = checkedToTotpCode(totpCodeRaw) | ||
if (totpCode instanceof Error) { | ||
return res.status(422).send({ error: totpCode.message }) | ||
} | ||
|
||
const authTokenRaw = req.body.authToken | ||
if (!authTokenRaw) { | ||
return res.status(422).send({ error: "Missing input" }) | ||
} | ||
|
||
const authToken = checkedToAuthToken(authTokenRaw) | ||
|
||
// FIXME return string currently when there is an error | ||
if (authToken === "Invalid value for AuthToken") { | ||
return res.status(422).send({ error: "Invalid value for AuthToken" }) | ||
} | ||
|
||
try { | ||
const result = await elevatingSessionWithTotp({ | ||
totpCode, | ||
authToken, | ||
}) | ||
if (result instanceof EmailCodeInvalidError) { | ||
recordExceptionInCurrentSpan({ error: result }) | ||
return res.status(401).send({ error: "invalid code" }) | ||
} | ||
if ( | ||
result instanceof EmailValidationSubmittedTooOftenError || | ||
result instanceof UserLoginIpRateLimiterExceededError | ||
) { | ||
recordExceptionInCurrentSpan({ error: result }) | ||
return res.status(429).send({ error: "too many requests" }) | ||
} | ||
if (result instanceof Error) { | ||
recordExceptionInCurrentSpan({ error: result }) | ||
return res.status(500).send({ error: result.message }) | ||
} | ||
return res.status(200).send() | ||
} catch (err) { | ||
recordExceptionInCurrentSpan({ error: err }) | ||
return res.status(500).send({ error: parseErrorMessageFromUnknown(err) }) | ||
} | ||
}) |
Check failure
Code scanning / CodeQL
Missing rate limiting High
authorization
authRouter.post("/email/login/cookie", async (req: Request, res: Response) => { | ||
const ip = req.originalIp | ||
|
||
const emailLoginIdRaw = req.body.emailLoginId | ||
if (!emailLoginIdRaw) { | ||
return res.status(422).send({ error: "Missing input" }) | ||
} | ||
|
||
const emailLoginId = checkedToEmailLoginId(emailLoginIdRaw) | ||
if (emailLoginId instanceof Error) { | ||
return res.status(422).send({ error: emailLoginId.message }) | ||
} | ||
|
||
const codeRaw = req.body.code | ||
if (!codeRaw) { | ||
return res.status(422).send({ error: "Missing input" }) | ||
} | ||
|
||
const code = checkedToEmailCode(codeRaw) | ||
if (code instanceof Error) { | ||
return res.status(422).send({ error: code.message }) | ||
} | ||
|
||
let loginResult: LoginWithEmailCookieResult | ApplicationError | ||
try { | ||
loginResult = await loginWithEmailCookie({ ip, emailFlowId: emailLoginId, code }) | ||
if (loginResult instanceof EmailCodeInvalidError) { | ||
recordExceptionInCurrentSpan({ error: loginResult }) | ||
return res.status(401).send({ error: "invalid code" }) | ||
} | ||
if ( | ||
loginResult instanceof EmailValidationSubmittedTooOftenError || | ||
loginResult instanceof UserLoginIpRateLimiterExceededError | ||
) { | ||
recordExceptionInCurrentSpan({ error: loginResult }) | ||
return res.status(429).send({ error: "too many requests" }) | ||
} | ||
if (loginResult instanceof Error) { | ||
recordExceptionInCurrentSpan({ error: loginResult }) | ||
return res.status(500).send({ error: loginResult.message }) | ||
} | ||
} catch (err) { | ||
recordExceptionInCurrentSpan({ error: err }) | ||
return res.status(500).send({ error: parseErrorMessageFromUnknown(err) }) | ||
} | ||
|
||
const { cookiesToSendBackToClient, kratosUserId, totpRequired } = loginResult | ||
let kratosCookies: KratosCookie | ||
try { | ||
kratosCookies = parseKratosCookies(cookiesToSendBackToClient) | ||
} catch (error) { | ||
recordExceptionInCurrentSpan({ error }) | ||
return res.status(500).send({ result: "Error parsing cookies" }) | ||
} | ||
const csrfCookie = kratosCookies.csrf() | ||
const kratosSessionCookie = kratosCookies.kratosSession() | ||
if (!csrfCookie || !kratosSessionCookie) { | ||
return res.status(500).send({ error: "Missing csrf or ory_kratos_session cookie" }) | ||
} | ||
res.cookie( | ||
kratosSessionCookie.name, | ||
kratosSessionCookie.value, | ||
kratosCookies.formatCookieOptions(kratosSessionCookie), | ||
) | ||
res.cookie( | ||
csrfCookie.name, | ||
csrfCookie.value, | ||
kratosCookies.formatCookieOptions(csrfCookie), | ||
) | ||
return res.status(200).send({ | ||
identity: { | ||
kratosUserId, | ||
totpRequired, | ||
}, | ||
}) | ||
}) |
Check failure
Code scanning / CodeQL
Missing rate limiting High
authorization
This route handler performs
authorization
authRouter.post("/phone/login", async (req: Request, res: Response) => { | ||
const ip = req.originalIp | ||
|
||
const codeRaw = req.body.code | ||
const phoneRaw = req.body.phone | ||
if (!codeRaw || !phoneRaw) { | ||
return res.status(400).send({ error: "missing inputs" }) | ||
} | ||
const code = validOneTimeAuthCodeValue(codeRaw) | ||
if (code instanceof Error) return res.status(400).send("invalid code") | ||
const phone = checkedToPhoneNumber(phoneRaw) | ||
if (phone instanceof Error) return res.status(400).send("invalid phone") | ||
|
||
const loginResp = await Authentication.loginWithPhoneToken({ | ||
phone, | ||
code, | ||
ip, | ||
}) | ||
if (loginResp instanceof Error) { | ||
return res.status(500).send({ error: mapError(loginResp).message }) | ||
} | ||
|
||
const { authToken, totpRequired } = loginResp | ||
|
||
return res.send({ | ||
authToken, | ||
totpRequired, | ||
}) | ||
}) |
Check failure
Code scanning / CodeQL
Missing rate limiting High
authorization
This route handler performs
No description provided.