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
167 lines
5.8 KiB
TypeScript
167 lines
5.8 KiB
TypeScript
// Copyright © 2023 Ory Corp
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
import { gen } from "../../../helpers"
|
|
import { routes as express } from "../../../helpers/express"
|
|
import * as oauth2 from "../../../helpers/oauth2"
|
|
import * as httpbin from "../../../helpers/httpbin"
|
|
import { TOTP } from "otpauth"
|
|
|
|
context("OIDC Provider 2FA", () => {
|
|
const client = {
|
|
auth_endpoint: "http://localhost:4744/oauth2/auth",
|
|
token_endpoint: "http://localhost:4744/oauth2/token",
|
|
id: Cypress.env("OIDC_DUMMY_CLIENT_ID"),
|
|
secret: Cypress.env("OIDC_DUMMY_CLIENT_SECRET"),
|
|
token_endpoint_auth_method: "client_secret_basic",
|
|
grant_types: ["authorization_code", "refresh_token"],
|
|
response_types: ["code", "id_token"],
|
|
scopes: ["openid", "offline", "email", "website"],
|
|
callbacks: [
|
|
"http://localhost:5555/callback",
|
|
"https://ory-network-httpbin-ijakee5waq-ez.a.run.app/anything",
|
|
],
|
|
}
|
|
|
|
;[
|
|
{
|
|
login: express.login,
|
|
settings: express.settings,
|
|
base: express.base,
|
|
profile: "oidc-provider-mfa",
|
|
app: "express" as "express",
|
|
},
|
|
].forEach(({ settings, login, profile, app, base }) => {
|
|
describe(`for app ${app}`, () => {
|
|
let email = gen.email()
|
|
let password = gen.password()
|
|
let secret
|
|
|
|
before(() => {
|
|
cy.useConfigProfile(profile)
|
|
cy.proxy(app)
|
|
|
|
email = gen.email()
|
|
password = gen.password()
|
|
|
|
cy.register({
|
|
email,
|
|
password,
|
|
fields: { "traits.website": "http://t1.local" },
|
|
})
|
|
cy.visit(settings)
|
|
|
|
cy.get('[data-testid="node/text/totp_secret_key/text"]').then(($e) => {
|
|
secret = $e.text().trim()
|
|
})
|
|
cy.get('input[name="totp_code"]').then(($e) => {
|
|
cy.wrap($e).type(
|
|
new TOTP({
|
|
secret,
|
|
}).generate(),
|
|
)
|
|
})
|
|
cy.get('*[name="method"][value="totp"]').click()
|
|
cy.expectSettingsSaved()
|
|
cy.getSession({
|
|
expectAal: "aal2",
|
|
expectMethods: ["password", "totp"],
|
|
})
|
|
|
|
cy.clearAllCookies()
|
|
})
|
|
|
|
it("should be be asked to sign in with 2fa if set up", () => {
|
|
let url = oauth2.getDefaultAuthorizeURL(client)
|
|
|
|
cy.get("body")
|
|
.then((body$) => {
|
|
// Credits https://github.com/suchipi, https://github.com/cypress-io/cypress/issues/944#issuecomment-444312914
|
|
const appWindow = body$[0].ownerDocument.defaultView
|
|
const appIframe = appWindow.parent.document.querySelector("iframe")
|
|
|
|
return new Promise((resolve) => {
|
|
appIframe.onload = () => resolve(undefined)
|
|
appWindow.location.href = url
|
|
})
|
|
})
|
|
.then(() => {
|
|
// kratos login ui
|
|
cy.get("[name=identifier]").type(email)
|
|
cy.get("[name=password]").type(password)
|
|
cy.get("[type=submit]").click()
|
|
|
|
cy.get('input[name="totp_code"]').then(($e) => {
|
|
cy.wrap($e).type(
|
|
new TOTP({
|
|
secret,
|
|
}).generate(),
|
|
)
|
|
})
|
|
cy.get('*[name="method"][value="totp"]').click()
|
|
|
|
// consent ui
|
|
cy.get("#openid").click()
|
|
cy.get("#offline").click()
|
|
cy.get("#accept").click()
|
|
|
|
let scope = ["offline", "openid"]
|
|
httpbin.checkToken(client, scope, (token: any) => {
|
|
expect(token).to.have.property("access_token")
|
|
expect(token).to.have.property("id_token")
|
|
expect(token).to.have.property("refresh_token")
|
|
expect(token).to.have.property("token_type")
|
|
expect(token).to.have.property("expires_in")
|
|
expect(token.scope).to.equal("offline openid")
|
|
let idToken = JSON.parse(
|
|
decodeURIComponent(
|
|
escape(window.atob(token.id_token.split(".")[1])),
|
|
),
|
|
)
|
|
expect(idToken).to.have.property("amr")
|
|
expect(idToken.amr).to.deep.equal(["password", "totp"])
|
|
})
|
|
|
|
// We shouldn't need to authenticate again
|
|
url = oauth2.getDefaultAuthorizeURL(client)
|
|
|
|
cy.get("body")
|
|
.then((body$) => {
|
|
// Credits https://github.com/suchipi, https://github.com/cypress-io/cypress/issues/944#issuecomment-444312914
|
|
const appWindow = body$[0].ownerDocument.defaultView
|
|
const appIframe =
|
|
appWindow.parent.document.querySelector("iframe")
|
|
|
|
return new Promise((resolve) => {
|
|
appIframe.onload = () => resolve(undefined)
|
|
appWindow.location.href = url
|
|
})
|
|
})
|
|
.then(() => {
|
|
// We get the consent screen instead of login
|
|
cy.get("#openid").click()
|
|
cy.get("#offline").click()
|
|
cy.get("#accept").click()
|
|
|
|
httpbin.checkToken(client, scope, (token: any) => {
|
|
expect(token).to.have.property("access_token")
|
|
expect(token).to.have.property("id_token")
|
|
expect(token).to.have.property("refresh_token")
|
|
expect(token).to.have.property("token_type")
|
|
expect(token).to.have.property("expires_in")
|
|
expect(token.scope).to.equal("offline openid")
|
|
let idToken = JSON.parse(
|
|
decodeURIComponent(
|
|
escape(window.atob(token.id_token.split(".")[1])),
|
|
),
|
|
)
|
|
expect(idToken).to.have.property("amr")
|
|
expect(idToken.amr).to.deep.equal(["password", "totp"])
|
|
})
|
|
})
|
|
})
|
|
})
|
|
})
|
|
})
|
|
})
|