snapshot of https://github.com/ory/kratos.git (master, 2026-09-15; blobs > 131072B omitted)
Some checks failed
CI / Run tests and lints (push) Failing after 29s
CI / Build CLI docs (push) Has been skipped
CI / Run Playwright end-to-end tests (mysql) (push) Failing after 3s
CI / Run Playwright end-to-end tests (postgres) (push) Failing after 2s
CI / Run Playwright end-to-end tests (sqlite) (push) Failing after 3s
CodeQL / Analyze (go) (push) Failing after 4s
CodeQL / Analyze (javascript) (push) Failing after 3s
Docker Image Scanners / scanners (push) Failing after 4s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Run end-to-end tests (postgres) (push) Failing after 19s
CI / Run end-to-end tests (sqlite) (push) Failing after 2s
CI / Run Playwright end-to-end tests (cockroach) (push) Failing after 2s
CI / Draft newsletter (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped

This commit is contained in:
upstream-mirror
2026-09-15 21:19:12 +05:00
commit c60cc7ca75
8092 changed files with 394160 additions and 0 deletions

View File

@@ -0,0 +1,382 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
import { Session } from "@ory/kratos-client"
import { gen, MOBILE_URL } from "../../../../helpers"
import { routes as express } from "../../../../helpers/express"
import { routes as react } from "../../../../helpers/react"
const Selectors = {
mobile: {
identifier: "[data-testid='field/identifier']",
recoveryEmail: "[data-testid='field/email']",
email: "[data-testid='traits.email']",
email2: "[data-testid='traits.email2']",
website: "[data-testid='traits.website']",
username: "[data-testid='traits.username']",
code: "[data-testid='field/code'] input",
recoveryCode: "[data-testid='code']",
submitCode: "[data-testid='field/method/code']",
resendCode: "[data-testid='field/resend/code']",
credentialSelection: "[data-testid='field/screen/credential-selection']",
submitRecovery: "[data-testid='field/method/code']",
codeHiddenMethod: "[data-testid='field/method/code']",
},
express: {
identifier: "[data-testid='login-flow-code'] input[name='identifier']",
recoveryEmail: "input[name=email]",
email: "[data-testid='node/input/traits.email'] input[name='traits.email']",
email2:
"[data-testid='node/input/traits.email2'] input[name='traits.email2']",
website:
"[data-testid='node/input/traits.website'] [name='traits.website']",
username:
"[data-testid='node/input/traits.username'] input[name='traits.username']",
code: "input[name='code']",
recoveryCode: "input[name=code]",
submitRecovery: "button[name=method][value=code]",
submitCode: "button[name='method'][value='code']",
resendCode: "button[name='resend'][value='code']",
codeHiddenMethod: "input[name='method'][value='code'][type='hidden']",
credentialSelection: "[name='screen'][value='credential-selection']",
},
react: {
identifier: "input[name='identifier']",
recoveryEmail: "input[name=email]",
email: "input[name='traits.email']",
email2: "input[name='traits.email2']",
website: "[name='traits.website']",
username: "input[name='traits.username']",
code: "input[name='code']",
recoveryCode: "input[name=code]",
submitRecovery: "button[name=method][value=code]",
submitCode: "button[name='method'][value='code']",
resendCode: "button[name='resend'][value='code']",
codeHiddenMethod: "input[name='method'][value='code'][type='hidden']",
credentialSelection: "[name='screen'][value='credential-selection']",
},
}
context("Registration success with code method", () => {
;[
{
route: express.registration,
login: express.login,
recovery: express.recovery,
app: "express" as "express",
profile: "two-steps",
},
{
route: react.registration,
login: react.login,
recovery: react.recovery,
app: "react" as "react",
profile: "two-steps",
},
{
route: MOBILE_URL + "/Registration",
login: MOBILE_URL + "/Login",
recovery: MOBILE_URL + "/Recovery",
app: "mobile" as "mobile",
profile: "two-steps",
},
].forEach(({ route, login, recovery, profile, app }) => {
describe(`for app ${app}`, () => {
before(() => {
cy.useConfigProfile(profile)
if (app !== "mobile") {
cy.proxy(app)
}
})
beforeEach(() => {
cy.deleteMail({ atLeast: 0 })
cy.clearAllCookies()
cy.visit(route)
})
it("should be able to resend the registration code", () => {
const email = gen.email()
const website = "https://www.example.org/"
cy.get(Selectors[app]["email"]).type(email)
cy.get(Selectors[app]["website"]).type(website)
cy.submitProfileForm(app)
cy.submitCodeForm(app)
cy.get('[data-testid="ui/message/1040005"]').should("be.visible")
cy.getRegistrationCodeFromEmail(email).then((code) =>
cy.wrap(code).as("code1"),
)
// cy.get(Selectors[app]["email"]).should("have.value", email)
cy.get(Selectors[app]["codeHiddenMethod"]).should("exist")
cy.get(Selectors[app]["resendCode"]).click()
cy.getRegistrationCodeFromEmail(email).then((code) => {
cy.wrap(code).as("code2")
})
cy.get("@code1").then((code1) => {
// previous code should not work
cy.get(Selectors[app]["code"]).clear()
cy.get(Selectors[app]["code"]).type(code1.toString())
cy.submitCodeForm(app)
cy.get('[data-testid="ui/message/4040003"]').should(
"contain.text",
"The registration code is invalid or has already been used. Please try again.",
)
})
// Navigate back and forth again
cy.get(Selectors[app]["credentialSelection"]).click()
cy.submitCodeForm(app)
cy.getRegistrationCodeFromEmail(email).then((code) => {
cy.wrap(code).as("code2")
})
cy.get("@code2").then((code2) => {
cy.get(Selectors[app]["code"]).clear()
cy.get(Selectors[app]["code"]).type(code2.toString())
cy.submitCodeForm(app)
})
if (app === "express") {
cy.url().should("match", /\/welcome/)
} else {
cy.get('[data-testid="session-content"]').should("contain", email)
}
if (app === "mobile") {
cy.get('[data-testid="session-token"]').should("not.be.empty")
cy.get('[data-testid="session-token"]').then((token) => {
cy.getSession({
expectAal: "aal1",
expectMethods: ["code"],
token: token.text(),
}).then((session) => {
cy.wrap(session).as("session")
})
})
} else {
cy.getSession({ expectAal: "aal1", expectMethods: ["code"] }).then(
(session) => {
cy.wrap(session).as("session")
},
)
}
cy.get<Session>("@session").then(({ identity }) => {
expect(identity.id).to.not.be.empty
expect(identity.traits.email).to.equal(email)
})
})
// playwright:migrated
it("should sign up and be logged in with session hook", () => {
if (app === "react") {
// This test is flaky on React, so we skip it for now.
return
}
const email = gen.email()
const website = "https://www.example.org/"
cy.get(Selectors[app]["email"]).type(email)
cy.get(Selectors[app]["website"]).type(website)
cy.submitProfileForm(app)
cy.submitCodeForm(app)
cy.get('[data-testid="ui/message/1040005"]').should("be.visible")
cy.getRegistrationCodeFromEmail(email).then((code) => {
cy.get(Selectors[app]["code"]).type(code)
cy.get(Selectors[app]["submitCode"]).click()
})
if (app === "mobile") {
cy.get('[data-testid="session-token"]').then((token) => {
cy.getSession({
expectAal: "aal1",
expectMethods: ["code"],
token: token.text(),
}).then((session) => {
cy.wrap(session).as("session")
})
})
cy.get('[data-testid="session-content"]').should("contain", email)
cy.get('[data-testid="session-token"]').should("not.be.empty")
} else {
cy.getSession({ expectAal: "aal1", expectMethods: ["code"] }).then(
(session) => {
cy.wrap(session).as("session")
},
)
}
cy.get<Session>("@session").then(({ identity }) => {
expect(identity.id).to.not.be.empty
expect(identity.traits.email).to.equal(email)
})
})
// playwright:migrated
it("should be able to sign up without session hook", () => {
if (app === "react") {
// This test is flaky on React, so we skip it for now.
return
}
cy.setPostCodeRegistrationHooks([])
const email = gen.email()
const website = "https://www.example.org/"
cy.get(Selectors[app]["email"]).type(email)
cy.get(Selectors[app]["website"]).type(website)
cy.submitProfileForm(app)
cy.submitCodeForm(app)
cy.get('[data-testid="ui/message/1040005"]').should("be.visible")
cy.getRegistrationCodeFromEmail(email).then((code) => {
cy.get(Selectors[app]["code"]).type(code)
cy.get(Selectors[app]["submitCode"]).click()
})
cy.visit(login)
cy.get(Selectors[app]["identifier"]).type(email)
cy.get(Selectors[app]["submitCode"]).click()
cy.getLoginCodeFromEmail(email).then((code) => {
cy.get(Selectors[app]["code"]).type(code)
cy.get(Selectors[app]["submitCode"]).click()
})
if (app === "mobile") {
cy.get('[data-testid="session-token"]').then((token) => {
cy.getSession({
expectAal: "aal1",
expectMethods: ["code"],
token: token.text(),
}).then((session) => {
cy.wrap(session).as("session")
})
})
cy.get('[data-testid="session-content"]').should("contain", email)
cy.get('[data-testid="session-token"]').should("not.be.empty")
} else {
cy.getSession({ expectAal: "aal1", expectMethods: ["code"] }).then(
(session) => {
cy.wrap(session).as("session")
},
)
}
cy.get<Session>("@session").then(({ identity }) => {
expect(identity.id).to.not.be.empty
expect(identity.traits.email).to.equal(email)
})
})
// Try keep this test as the last one, as it updates the identity schema.
it("should be able to use multiple identifiers to signup with and sign in to", () => {
cy.setPostCodeRegistrationHooks([
{
hook: "session",
},
])
// Setup complex schema
cy.setIdentitySchema(
"file://test/e2e/profiles/code/identity.complex.traits.schema.json",
)
cy.visit(route)
cy.get(Selectors[app]["username"]).type(Math.random().toString(36))
const email = gen.email()
cy.get(Selectors[app]["email"]).type(email)
const email2 = gen.email()
cy.get(Selectors[app]["email2"]).type(email2)
cy.submitProfileForm(app)
cy.submitCodeForm(app)
cy.get('[data-testid="ui/message/1040005"]').should("be.visible")
// intentionally use email 1 to sign up for the account
cy.getRegistrationCodeFromEmail(email, { expectedCount: 1 }).then(
(code) => {
cy.get(Selectors[app]["code"]).type(code)
cy.get(Selectors[app]["submitCode"]).click()
},
)
if (app === "mobile") {
cy.visit(MOBILE_URL + "/Home")
cy.get('*[data-testid="logout"]').click()
} else {
cy.logout()
}
// There are verification emails from the registration process in the inbox that we need to deleted
// for the assertions below to pass.
cy.deleteMail({ atLeast: 1 })
// Attempt to sign in with email 2 (should fail)
cy.visit(login)
cy.get(Selectors[app]["identifier"]).type(email2)
cy.get(Selectors[app]["submitCode"]).click()
cy.getLoginCodeFromEmail(email2, {
expectedCount: 1,
}).then((code) => {
cy.get(Selectors[app]["code"]).type(code)
cy.get(Selectors[app]["submitCode"]).click()
})
if (app === "mobile") {
cy.get('[data-testid="session-token"]').then((token) => {
cy.getSession({
expectAal: "aal1",
expectMethods: ["code"],
token: token.text(),
}).then((session) => {
cy.wrap(session).as("session")
})
})
cy.get('[data-testid="session-content"]').should("contain", email)
cy.get('[data-testid="session-token"]').should("not.be.empty")
} else {
cy.getSession({ expectAal: "aal1", expectMethods: ["code"] }).then(
(session) => {
cy.wrap(session).as("session")
},
)
}
cy.get<Session>("@session").then(({ identity }) => {
expect(identity.id).to.not.be.empty
expect(identity.verifiable_addresses).to.have.length(2)
expect(
identity.verifiable_addresses.filter((v) => v.value === email)[0]
.status,
).to.equal("completed")
expect(
identity.verifiable_addresses.filter((v) => v.value === email2)[0]
.status,
).to.equal("completed")
expect(identity.traits.email).to.equal(email)
})
})
})
})
})

View File

@@ -0,0 +1,225 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
import { appPrefix, gen, website } from "../../../../helpers"
import { routes as express } from "../../../../helpers/express"
import { routes as react } from "../../../../helpers/react"
import { testFlowWebhook } from "../../../../helpers/webhook"
context("Social Sign Up Successes", () => {
;[
{
login: react.login,
registration: react.registration,
app: "react" as "react",
profile: "two-steps",
},
{
login: express.login,
registration: express.registration,
app: "express" as "express",
profile: "two-steps",
},
].forEach(({ registration, login, profile, app }) => {
describe(`for app ${app}`, () => {
before(() => {
cy.useConfigProfile(profile)
cy.proxy(app)
})
beforeEach(() => {
cy.clearAllCookies()
cy.visit(registration)
cy.setIdentitySchema(
"file://test/e2e/profiles/oidc/identity.traits.schema.json",
)
})
const shouldSession = (email) => (session) => {
const { identity } = session
expect(identity.id).to.not.be.empty
expect(identity.traits.website).to.equal(website)
expect(identity.traits.email).to.equal(email)
}
it("should be able to sign up with incomplete data and finally be signed in", () => {
const email = gen.email()
cy.registerOidc({
app,
email,
expectSession: false,
route: registration,
})
cy.get("#registration-password").should("not.exist")
cy.get(appPrefix(app) + '[name="traits.email"]').should(
"have.value",
email,
)
cy.get('[data-testid="ui/message/4000002"]').should(
"contain.text",
"Property website is missing",
)
cy.get('[name="traits.consent"][type="checkbox"]')
.siblings("label")
.click()
cy.get('[name="traits.newsletter"][type="checkbox"]')
.siblings("label")
.click()
cy.get('[name="traits.website"]').type("http://s")
cy.get('[name="provider"]')
.should("have.length", 1)
.should("have.value", "hydra")
.should("contain.text", "Continue")
.click()
cy.get("#registration-password").should("not.exist")
cy.get('[name="traits.email"]').should("have.value", email)
cy.get('[name="traits.website"]').should("have.value", "http://s")
cy.get('[data-testid="ui/message/4000003"]').should(
"contain.text",
"length must be >= 10",
)
cy.get('[name="traits.website"]')
.should("have.value", "http://s")
.clear()
.type(website)
cy.get('[name="traits.consent"]').should("be.checked")
cy.get('[name="traits.newsletter"]').should("be.checked")
cy.triggerOidc(app)
cy.location("pathname").should((loc) => {
expect(loc).to.be.oneOf(["/welcome", "/", "/sessions"])
})
cy.getSession().should((session) => {
shouldSession(email)(session)
expect(session.identity.traits.consent).to.equal(true)
})
})
it("should pass transient_payload to webhook", () => {
testFlowWebhook(
(hooks) =>
cy.setupHooks("registration", "after", "oidc", [
...hooks,
{ hook: "session" },
]),
() => {
const email = gen.email()
cy.registerOidc({
app,
email,
website,
route: registration,
})
cy.getSession().should(shouldSession(email))
},
)
})
it("should be able to sign up with complete data", () => {
const email = gen.email()
cy.registerOidc({ app, email, website, route: registration })
cy.getSession().should(shouldSession(email))
})
it("should be able to convert a sign up flow to a sign in flow", () => {
const email = gen.email()
cy.registerOidc({ app, email, website, route: registration })
cy.logout()
cy.noSession()
cy.visit(registration)
cy.triggerOidc(app)
cy.location("pathname").should((path) => {
expect(path).to.oneOf(["/", "/welcome", "/sessions"])
})
cy.getSession().should(shouldSession(email))
})
it("should be able to convert a sign in flow to a sign up flow", () => {
cy.setIdentitySchema(
"file://test/e2e/profiles/oidc/identity-required.traits.schema.json",
)
const email = gen.email()
cy.visit(login)
cy.triggerOidc(app)
cy.get("#username").clear().type(email)
cy.get("#remember").click()
cy.get("#accept").click()
cy.get('[name="scope"]').each(($el) => cy.wrap($el).click())
cy.get("#remember").click()
cy.get("#accept").click()
cy.get('[data-testid="ui/message/4000002"]').should(
"contain.text",
"Property website is missing",
)
cy.get('[name="traits.website"]').type("http://s")
cy.triggerOidc(app)
cy.get('[data-testid="ui/message/4000003"]').should(
"contain.text",
"length must be >= 10",
)
cy.get('[name="traits.requirednested"]').should("not.exist")
cy.get('[name="traits.requirednested.a"]').siblings("label").click()
cy.get('[name="traits.consent"]').siblings("label").click()
cy.get('[name="traits.website"]')
.should("have.value", "http://s")
.clear()
.type(website)
cy.triggerOidc(app)
cy.location("pathname").should("not.contain", "/registration")
cy.getSession().should(shouldSession(email))
})
it("should be able to sign up with redirects", () => {
const email = gen.email()
cy.registerOidc({
app,
email,
website,
route: registration + "?return_to=https://www.example.org/",
})
cy.location("href").should("eq", "https://www.example.org/")
cy.logout()
})
it("should be able to register with upstream parameters", () => {
const email = gen.email()
cy.intercept("GET", "**/oauth2/auth*").as("getHydraRegistration")
cy.visit(registration + "?return_to=https://www.example.org/")
// hd is forwarded; login_hint is never forwarded because a
// caller-supplied value cannot be trusted (HackerOne #3239672,
// ory-corp/cloud#8955).
cy.addInputElement("form", "upstream_parameters.hd", "ory.sh")
cy.addInputElement("form", "upstream_parameters.login_hint", email)
cy.triggerOidc(app)
// once a request to getHydraRegistration responds, 'cy.wait' will resolve
cy.wait("@getHydraRegistration")
.its("request.url")
.should("include", "hd=ory.sh")
.and("not.include", "login_hint")
})
})
})
})

View File

@@ -0,0 +1,117 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
import { appPrefix, APP_URL, gen } from "../../../../helpers"
import { routes as express } from "../../../../helpers/express"
import { routes as react } from "../../../../helpers/react"
context("Registration success with two-step signup", () => {
;[
{
route: express.registration,
app: "express" as "express",
profile: "two-steps",
},
{
route: react.registration,
app: "react" as "react",
profile: "two-steps",
},
].forEach(({ route, profile, app }) => {
describe(`for app ${app}`, () => {
before(() => {
cy.useConfigProfile(profile)
cy.proxy(app)
})
beforeEach(() => {
cy.deleteMail()
cy.clearAllCookies()
cy.visit(route)
cy.enableVerification()
if (app === "express") {
cy.enableVerificationUIAfterRegistration("password")
}
})
// playwright:migrated
it("should sign up and be logged in", () => {
const email = gen.email()
const password = gen.password()
const website = "https://www.example.org/"
// Fill out step one forms
cy.get(appPrefix(app) + 'input[name="traits"]').should("not.exist")
cy.get('input[name="traits.email"]').type("someone@foo")
cy.get('input[name="traits.website"]').type(website)
cy.get('[name="method"][value="profile"]').click()
// navigate back, fill traits again
cy.get('[name="screen"][value="previous"]').click()
cy.get('input[name="traits.email"]').type(
"{selectall}{backspace}" + email,
)
cy.get('[name="method"][value="profile"]').click()
// Fill out step two forms
cy.get('input[name="password"]').type(password)
cy.get('[name="method"][value="password"]').click()
if (app === "express") {
cy.get('a[href*="sessions"]').click()
}
cy.get("pre").should("contain.text", email)
cy.getSession().should((session) => {
const { identity } = session
expect(identity.id).to.not.be.empty
expect(identity.schema_id).to.equal("default")
expect(identity.schema_url).to.equal(`${APP_URL}/schemas/ZGVmYXVsdA`)
expect(identity.traits.website).to.equal(website)
expect(identity.traits.email).to.equal(email)
})
})
// playwright:migrated
it("should handle form errors", () => {
const email = gen.email()
const password = gen.password()
const websiteTooShort = "a://b"
const website = "https://www.example.com"
// Fill out step one forms
cy.get(appPrefix(app) + 'input[name="traits"]').should("not.exist")
cy.get('input[name="traits.email"]').type(email)
cy.get('input[name="traits.website"]').type(websiteTooShort)
cy.get('[name="method"][value="profile"]').click()
// Assert form errors
cy.get('[data-testid="ui/message/4000003"]').should("to.exist")
// Enter correct value
cy.get('input[name="traits.website"]').type(
"{selectall}{backspace}" + website,
)
cy.get('[name="method"][value="profile"]').click()
// Fill out step two forms
cy.get('input[name="password"]').type(password)
cy.get('[name="method"][value="password"]').click()
if (app === "express") {
cy.get('a[href*="sessions"]').click()
}
cy.get("pre").should("contain.text", email)
cy.getSession().should((session) => {
const { identity } = session
expect(identity.id).to.not.be.empty
expect(identity.schema_id).to.equal("default")
expect(identity.schema_url).to.equal(`${APP_URL}/schemas/ZGVmYXVsdA`)
expect(identity.traits.website).to.equal(website)
expect(identity.traits.email).to.equal(email)
})
})
})
})
})