-
-
Notifications
You must be signed in to change notification settings - Fork 40
/
Login.tsx
152 lines (139 loc) · 5.66 KB
/
Login.tsx
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
import { LoginFlow, UpdateLoginFlowBody } from "@ory/client"
import { UserAuthCard } from "@ory/elements"
import { useCallback, useEffect, useState } from "react"
import { useNavigate, useSearchParams } from "react-router-dom"
import { sdk, sdkError } from "./sdk"
/**
* Login is a React component that renders the login form using Ory Elements.
* It is used to handle the login flow for a variety of authentication methods
* and authentication levels (e.g. Single-Factor and Two-Factor)
*
* The Login component also handles the OAuth2 login flow (as an OAuth2 provider)
* For more information regarding OAuth2 login, please see the following documentation:
* https://www.ory.sh/docs/oauth2-oidc/custom-login-consent/flow
*
*/
export const Login = (): JSX.Element => {
const [flow, setFlow] = useState<LoginFlow | null>(null)
const [searchParams, setSearchParams] = useSearchParams()
// The aal is set as a query parameter by your Ory project
// aal1 is the default authentication level (Single-Factor)
// aal2 is a query parameter that can be used to request Two-Factor authentication
// https://www.ory.sh/docs/kratos/mfa/overview
const aal2 = searchParams.get("aal2")
// The login_challenge is a query parameter set by the Ory OAuth2 login flow
// Switching between pages should keep the login_challenge in the URL so that the
// OAuth flow can be completed upon completion of another flow (e.g. Registration).
// https://www.ory.sh/docs/oauth2-oidc/custom-login-consent/flow
const loginChallenge = searchParams.get("login_challenge")
// The return_to is a query parameter is set by you when you would like to redirect
// the user back to a specific URL after login is successful
// In most cases it is not necessary to set a return_to if the UI business logic is
// handled by the SPA.
//
// In OAuth flows this value might be ignored in favor of keeping the OAuth flow
// intact between multiple flows (e.g. Login -> Recovery -> Settings -> OAuth2 Consent)
// https://www.ory.sh/docs/oauth2-oidc/identity-provider-integration-settings
const returnTo = searchParams.get("return_to")
const navigate = useNavigate()
// Get the flow based on the flowId in the URL (.e.g redirect to this page after flow initialized)
const getFlow = useCallback(
(flowId: string) =>
sdk
// the flow data contains the form fields, error messages and csrf token
.getLoginFlow({ id: flowId })
.then(({ data: flow }) => setFlow(flow))
.catch(sdkErrorHandler),
[],
)
// initialize the sdkError for generic handling of errors
const sdkErrorHandler = sdkError(getFlow, setFlow, "/login", true)
// Create a new login flow
const createFlow = () => {
sdk
.createBrowserLoginFlow({
refresh: true,
aal: aal2 ? "aal2" : "aal1",
...(loginChallenge && { loginChallenge: loginChallenge }),
...(returnTo && { returnTo: returnTo }),
})
// flow contains the form fields and csrf token
.then(({ data: flow }) => {
// Update URI query params to include flow id
setSearchParams({ ["flow"]: flow.id })
// Set the flow data
setFlow(flow)
})
.catch(sdkErrorHandler)
}
// submit the login form data to Ory
const submitFlow = (body: UpdateLoginFlowBody) => {
// something unexpected went wrong and the flow was not set
if (!flow) return navigate("/login", { replace: true })
// we submit the flow to Ory with the form data
sdk
.updateLoginFlow({ flow: flow.id, updateLoginFlowBody: body })
.then(() => {
// we successfully submitted the login flow, so lets redirect to the dashboard
navigate("/", { replace: true })
})
.catch(sdkErrorHandler)
}
useEffect(() => {
// we might redirect to this page after the flow is initialized, so we check for the flowId in the URL
const flowId = searchParams.get("flow")
// the flow already exists
if (flowId) {
getFlow(flowId).catch(createFlow) // if for some reason the flow has expired, we need to get a new one
return
}
// we assume there was no flow, so we create a new one
createFlow()
}, [])
// we check if the flow is set, if not we show a loading indicator
return flow ? (
// we render the login form using Ory Elements
<UserAuthCard
flowType={"login"}
// we always need the flow data which populates the form fields and error messages dynamically
flow={flow}
// the login card should allow the user to go to the registration page and the recovery page
additionalProps={{
forgotPasswordURL: {
handler: () => {
const search = new URLSearchParams()
flow.return_to && search.set("return_to", flow.return_to)
navigate(
{
pathname: "/recovery",
search: search.toString(),
},
{ replace: true },
)
},
},
signupURL: {
handler: () => {
const search = new URLSearchParams()
flow.return_to && search.set("return_to", flow.return_to)
flow.oauth2_login_challenge &&
search.set("login_challenge", flow.oauth2_login_challenge)
navigate(
{
pathname: "/registration",
search: search.toString(),
},
{ replace: true },
)
},
},
}}
// we might need webauthn support which requires additional js
includeScripts={true}
// we submit the form data to Ory
onSubmit={({ body }) => submitFlow(body as UpdateLoginFlowBody)}
/>
) : (
<div>Loading...</div>
)
}