Skip to content
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

feat: support identifier first auth #187

Merged
merged 5 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"identities.messages.1010005": "Verify",
"identities.messages.1010006": "Authentication code",
"identities.messages.1010007": "Backup recovery code",
"identities.messages.1010008": "Use security key",
"identities.messages.1010008": "Sign in with hardware key",
"identities.messages.1010009": "Use Authenticator",
"identities.messages.1010010": "Use backup recovery code",
"identities.messages.1010011": "Continue with security key",
Expand Down Expand Up @@ -131,7 +131,7 @@
"identities.messages.4070006": "The verification code is invalid or has already been used. Please try again.",
"identities.messages.5000001": "{reason}",
"login.forgot-password": "Forgot password?",
"login.logged-in-as-label": "You're logged in as:",
"login.logged-in-as-label": "You are using:",
"login.logout-button": "Logout",
"login.logout-label": "Something's not working?",
"login.registration-button": "Sign up",
Expand Down
68 changes: 36 additions & 32 deletions src/react-components/ory/helpers/user-auth-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,19 +69,9 @@ export const UserAuthForm = ({
formFilterOverride,
className,
...props
}: UserAuthFormProps): JSX.Element => (
<form
className={cn(className, formStyle)}
action={flow.ui.action}
method={flow.ui.method}
onKeyDown={(e) => {
if (e.key === "Enter" && !submitOnEnter) {
e.stopPropagation()
e.preventDefault()
}
}}
{...(onSubmit && {
onSubmit: (event: React.FormEvent<HTMLFormElement>) => {
}: UserAuthFormProps): JSX.Element => {
const submitHandler = onSubmit
? (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault()

const form = event.currentTarget
Expand All @@ -108,23 +98,37 @@ export const UserAuthForm = ({
}

onSubmit({ body, event })
},
})}
{...props}
>
<>
{/*always add csrf token and other hidden fields to form*/}
<FilterFlowNodes
filter={
formFilterOverride ?? {
nodes: flow.ui.nodes,
groups: "default", // we only want to map hidden default fields here
attributes: "hidden",
}
}
: undefined

return (
<form
className={cn(className, formStyle)}
action={flow.ui.action}
method={flow.ui.method}
onKeyDown={(e) => {
if (e.key === "Enter" && !submitOnEnter) {
e.stopPropagation()
e.preventDefault()
}
includeCSRF={true}
/>
{children}
</>
</form>
)
}}
onSubmit={submitHandler}
{...props}
>
<>
{/*always add csrf token and other hidden fields to form*/}
<FilterFlowNodes
filter={
formFilterOverride ?? {
nodes: flow.ui.nodes,
groups: ["default"], // we only want to map hidden default fields here
attributes: "hidden",
}
}
includeCSRF={true}
/>
{children}
</>
</form>
)
}
2 changes: 2 additions & 0 deletions src/react-components/ory/helpers/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ export const hasGroup = (group: string) => (nodes: UiNode[]) =>

export const hasOidc = hasGroup("oidc")
export const hasPassword = hasGroup("password")
export const hasDefault = hasGroup("default")
export const hasProfile = hasGroup("profile")
export const hasWebauthn = hasGroup("webauthn")
export const hasPasskey = hasGroup("passkey")
export const hasIdentifierFirst = hasGroup("identifier_first")
export const hasLookupSecret = hasGroup("lookup_secret")
export const hasTotp = hasGroup("totp")
export const hasCode = hasGroup("code")
Expand Down
7 changes: 4 additions & 3 deletions src/react-components/ory/sections/auth-code-section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const AuthCodeSection = ({
<FilterFlowNodes
filter={{
nodes: nodes,
groups: ["code"],
groups: ["code", "identifier_first"],
// we don't want to map the default group twice
// the form already maps hidden fields under the default group
// we are only interested in hidden fields that are under the code group
Expand All @@ -36,9 +36,9 @@ export const AuthCodeSection = ({
<FilterFlowNodes
filter={{
nodes: nodes,
groups: "code",
groups: ["code"],
withoutDefaultAttributes: true,
excludeAttributes: ["hidden", "button", "submit"], // the form will take care of default (csrf) hidden fields
excludeAttributeTypes: ["hidden", "button", "submit"], // the form will take care of default (csrf) hidden fields
}}
/>
{/* include hidden here because we want to have resend support */}
Expand All @@ -49,6 +49,7 @@ export const AuthCodeSection = ({
groups: "code",
withoutDefaultAttributes: true,
attributes: ["button", "submit"],
excludeAttributeTypes: ["hidden"],
}}
/>
</div>
Expand Down
32 changes: 21 additions & 11 deletions src/react-components/ory/sections/link-section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,32 @@ export interface LinkSectionProps {
* - https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation
*/
export const LinkSection = ({ nodes }: LinkSectionProps): JSX.Element => (
<div className={gridStyle({ gap: 32 })}>
<div className={gridStyle({ gap: 16 })}>
<>
<FilterFlowNodes
filter={{
nodes: nodes,
groups: ["link", "code", "identifier_first"],
attributes: ["hidden"],
}}
/>
<div className={gridStyle({ gap: 32 })}>
<div className={gridStyle({ gap: 16 })}>
<FilterFlowNodes
filter={{
nodes: nodes,
groups: ["link", "code"],
excludeAttributeTypes: ["submit", "hidden"],
}}
/>
</div>
<FilterFlowNodes
filter={{
nodes: nodes,
groups: ["link", "code"],
excludeAttributes: "submit",
attributes: "submit",
excludeAttributeTypes: ["hidden"],
}}
/>
</div>
<FilterFlowNodes
filter={{
nodes: nodes,
groups: ["link", "code"],
attributes: "submit",
}}
/>
</div>
</>
)
2 changes: 1 addition & 1 deletion src/react-components/ory/sections/logged-info.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const LoggedInInfo = ({ flow }: IdentifierInfoProps) => {
<div className={identifierStyle}>
<FormattedMessage
id="login.logged-in-as-label"
defaultMessage="You're logged in as:"
defaultMessage="You are using:"
/>
<div className={identifierNameStyle}>{identifier.value}</div>
</div>
Expand Down
83 changes: 59 additions & 24 deletions src/react-components/ory/sections/login-section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,46 +5,81 @@ import { FormattedMessage } from "react-intl"
import { gridStyle } from "../../../theme"
import { ButtonLink, CustomHref } from "../../button-link"
import { FilterFlowNodes } from "../helpers/filter-flow-nodes"
import { hasPassword } from "../helpers/utils"
import { hasPassword, hasIdentifierFirst } from "../helpers/utils"
import { SelfServiceFlow } from "../helpers/types"

export interface LoginSectionProps {
nodes: UiNode[]
forgotPasswordURL?: CustomHref | string
}

export const IdentifierFirstLoginSection = (
flow: SelfServiceFlow,
): JSX.Element | null => {
const nodes = flow.ui.nodes
return hasIdentifierFirst(nodes) ? (
<div className={gridStyle({ gap: 32 })}>
<FilterFlowNodes
filter={{
nodes: nodes,
groups: ["default", "identifier_first"],
excludeAttributeTypes: ["submit", "hidden"],
}}
/>
<FilterFlowNodes
filter={{
nodes: nodes,
groups: ["identifier_first"],
attributes: "submit",
}}
/>
</div>
) : null
}

export const LoginSection = ({
nodes,
forgotPasswordURL,
}: LoginSectionProps): JSX.Element | null => {
return hasPassword(nodes) ? (
<div className={gridStyle({ gap: 32 })}>
<div className={gridStyle({ gap: 16 })}>
<>
<FilterFlowNodes
filter={{
nodes: nodes,
groups: ["link", "code", "identifier_first"],
attributes: ["hidden"],
}}
/>
<div className={gridStyle({ gap: 32 })}>
<div className={gridStyle({ gap: 16 })}>
<FilterFlowNodes
filter={{
nodes: nodes,
groups: ["default", "password"],
excludeAttributeTypes: ["submit", "hidden"],
}}
/>
{forgotPasswordURL && (
<ButtonLink
data-testid="forgot-password-link"
href={forgotPasswordURL}
>
<FormattedMessage
id="login.forgot-password"
defaultMessage="Forgot password?"
/>
</ButtonLink>
)}
</div>
<FilterFlowNodes
filter={{
nodes: nodes,
groups: ["default", "password"],
excludeAttributes: ["submit", "hidden"],
groups: ["password"],
attributes: "submit",
excludeAttributeTypes: ["hidden"],
}}
/>
{forgotPasswordURL && (
<ButtonLink
data-testid="forgot-password-link"
href={forgotPasswordURL}
>
<FormattedMessage
id="login.forgot-password"
defaultMessage="Forgot password?"
/>
</ButtonLink>
)}
</div>
<FilterFlowNodes
filter={{
nodes: nodes,
groups: ["password"],
attributes: "submit",
}}
/>
</div>
</>
) : null
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const LookupSecretSettingsSection = ({
return hasLookupSecret(flow.ui.nodes) ? (
<div className={gridStyle({ gap: 32 })}>
<FilterFlowNodes
filter={{ ...filter, excludeAttributes: "submit,button" }}
filter={{ ...filter, excludeAttributeTypes: "submit,button" }}
/>
<FilterFlowNodes
filter={{ ...filter, attributes: "submit,button" }}
Expand Down
5 changes: 3 additions & 2 deletions src/react-components/ory/sections/oidc-section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const OIDCSection = (flow: SelfServiceFlow): JSX.Element | null => {
nodes: flow.ui.nodes,
groups: "oidc",
withoutDefaultGroup: true,
excludeAttributes: "submit",
excludeAttributeTypes: "submit",
}).length > 0

return hasOidc(flow.ui.nodes) ? (
Expand All @@ -24,7 +24,7 @@ export const OIDCSection = (flow: SelfServiceFlow): JSX.Element | null => {
nodes: flow.ui.nodes,
groups: "oidc",
withoutDefaultGroup: true,
excludeAttributes: "submit",
excludeAttributeTypes: ["submit"],
}}
/>
</div>
Expand All @@ -35,6 +35,7 @@ export const OIDCSection = (flow: SelfServiceFlow): JSX.Element | null => {
nodes: flow.ui.nodes,
groups: "oidc",
attributes: "submit",
withoutDefaultGroup: true,
}}
/>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,8 @@ export const PasskeySettingsSection = ({
return hasPasskey(flow.ui.nodes) ? (
<div className={gridStyle({ gap: 32 })}>
<FilterFlowNodes
filter={{ ...filter, excludeAttributes: "onclick,button" }}
filter={{ ...filter, excludeAttributeTypes: "onclick,button" }}
/>

<FilterFlowNodes
filter={{ ...filter, attributes: "onclick,button" }}
buttonOverrideProps={{ fullWidth: false }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const PasswordSettingsSection = ({
return hasPassword(flow.ui.nodes) ? (
<div className={gridStyle({ gap: 32 })}>
<FilterFlowNodes
filter={{ ...filter, excludeAttributes: "submit,button" }}
filter={{ ...filter, excludeAttributeTypes: "submit,button" }}
/>
<FilterFlowNodes
filter={{ ...filter, attributes: "submit,button" }}
Expand Down
Loading
Loading