mirror of
https://github.com/zitadel/zitadel.git
synced 2025-12-11 09:12:16 +00:00
split invite, email verification
This commit is contained in:
105
apps/login/cypress/integration/invite.cy.ts
Normal file
105
apps/login/cypress/integration/invite.cy.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import { stub } from "../support/mock";
|
||||
|
||||
describe("verify invite", () => {
|
||||
beforeEach(() => {
|
||||
stub("zitadel.org.v2.OrganizationService", "ListOrganizations", {
|
||||
data: {
|
||||
details: {
|
||||
totalResult: 1,
|
||||
},
|
||||
result: [{ id: "256088834543534543" }],
|
||||
},
|
||||
});
|
||||
|
||||
stub("zitadel.user.v2.UserService", "ListAuthenticationMethodTypes", {
|
||||
data: {
|
||||
authMethodTypes: [], // user with no auth methods was invited
|
||||
},
|
||||
});
|
||||
|
||||
stub("zitadel.user.v2.UserService", "GetUserByID", {
|
||||
data: {
|
||||
user: {
|
||||
userId: "221394658884845598",
|
||||
state: 1,
|
||||
username: "john@zitadel.com",
|
||||
loginNames: ["john@zitadel.com"],
|
||||
preferredLoginName: "john@zitadel.com",
|
||||
human: {
|
||||
userId: "221394658884845598",
|
||||
state: 1,
|
||||
username: "john@zitadel.com",
|
||||
loginNames: ["john@zitadel.com"],
|
||||
preferredLoginName: "john@zitadel.com",
|
||||
profile: {
|
||||
givenName: "John",
|
||||
familyName: "Doe",
|
||||
avatarUrl: "https://zitadel.com/avatar.jpg",
|
||||
},
|
||||
email: {
|
||||
email: "john@zitadel.com",
|
||||
isVerified: true, // email needs to be verified
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
stub("zitadel.session.v2.SessionService", "CreateSession", {
|
||||
data: {
|
||||
details: {
|
||||
sequence: 859,
|
||||
changeDate: new Date("2024-04-04T09:40:55.577Z"),
|
||||
resourceOwner: "220516472055706145",
|
||||
},
|
||||
sessionId: "221394658884845598",
|
||||
sessionToken:
|
||||
"SDMc7DlYXPgwRJ-Tb5NlLqynysHjEae3csWsKzoZWLplRji0AYY3HgAkrUEBqtLCvOayLJPMd0ax4Q",
|
||||
challenges: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
stub("zitadel.session.v2.SessionService", "GetSession", {
|
||||
data: {
|
||||
session: {
|
||||
id: "221394658884845598",
|
||||
creationDate: new Date("2024-04-04T09:40:55.577Z"),
|
||||
changeDate: new Date("2024-04-04T09:40:55.577Z"),
|
||||
sequence: 859,
|
||||
factors: {
|
||||
user: {
|
||||
id: "221394658884845598",
|
||||
loginName: "john@zitadel.com",
|
||||
},
|
||||
password: undefined,
|
||||
webAuthN: undefined,
|
||||
intent: undefined,
|
||||
},
|
||||
metadata: {},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it.only("shows authenticators after successful invite verification", () => {
|
||||
stub("zitadel.user.v2.UserService", "VerifyInviteCode");
|
||||
|
||||
cy.visit("/verify?userId=221394658884845598&code=abc&invite=true");
|
||||
cy.location("pathname", { timeout: 10_000 }).should(
|
||||
"eq",
|
||||
"/authenticator/set",
|
||||
);
|
||||
});
|
||||
|
||||
it("shows an error if invite code validation failed", () => {
|
||||
stub("zitadel.user.v2.UserService", "VerifyInviteCode", {
|
||||
code: 3,
|
||||
error: "error validating code",
|
||||
});
|
||||
|
||||
// TODO: Avoid uncaught exception in application
|
||||
cy.once("uncaught:exception", () => false);
|
||||
cy.visit("/verify?userId=221394658884845598&code=abc&invite=true");
|
||||
cy.contains("Could not verify invite", { timeout: 10_000 });
|
||||
});
|
||||
});
|
||||
@@ -165,6 +165,7 @@ describe("login", () => {
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("should redirect a user with passwordless authentication to /passkey", () => {
|
||||
cy.visit("/loginname?loginName=john%40zitadel.com&submit=true");
|
||||
cy.location("pathname", { timeout: 10_000 }).should("eq", "/passkey");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { stub } from "../support/mock";
|
||||
|
||||
describe("verify invite", () => {
|
||||
describe("verify email", () => {
|
||||
beforeEach(() => {
|
||||
stub("zitadel.org.v2.OrganizationService", "ListOrganizations", {
|
||||
data: {
|
||||
@@ -13,7 +13,7 @@ describe("verify invite", () => {
|
||||
|
||||
stub("zitadel.user.v2.UserService", "ListAuthenticationMethodTypes", {
|
||||
data: {
|
||||
authMethodTypes: [],
|
||||
authMethodTypes: [1], // set one method such that we know that the user was not invited
|
||||
},
|
||||
});
|
||||
|
||||
@@ -83,83 +83,16 @@ describe("verify invite", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it.only("shows authenticators after successful invite verification", () => {
|
||||
stub("zitadel.user.v2.UserService", "GetUserByID", {
|
||||
data: {
|
||||
user: {
|
||||
userId: "221394658884845598",
|
||||
state: 1,
|
||||
username: "john@zitadel.com",
|
||||
loginNames: ["john@zitadel.com"],
|
||||
preferredLoginName: "john@zitadel.com",
|
||||
human: {
|
||||
userId: "221394658884845598",
|
||||
state: 1,
|
||||
username: "john@zitadel.com",
|
||||
loginNames: ["john@zitadel.com"],
|
||||
preferredLoginName: "john@zitadel.com",
|
||||
profile: {
|
||||
givenName: "John",
|
||||
familyName: "Doe",
|
||||
avatarUrl: "https://zitadel.com/avatar.jpg",
|
||||
},
|
||||
email: {
|
||||
email: "john@zitadel.com",
|
||||
isVerified: true, // email needs to be verified
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
// it("shows password and passkey method after successful invite verification", () => {
|
||||
// stub("zitadel.user.v2.UserService", "VerifyEmail");
|
||||
// cy.visit("/verify?userId=221394658884845598&code=abc");
|
||||
// cy.location("pathname", { timeout: 10_000 }).should(
|
||||
// "eq",
|
||||
// "/authenticator/set",
|
||||
// );
|
||||
// });
|
||||
|
||||
stub("zitadel.user.v2.UserService", "VerifyInviteCode");
|
||||
cy.visit("/verify?userId=221394658884845598&code=abc&invite=true");
|
||||
cy.location("pathname", { timeout: 10_000 }).should(
|
||||
"eq",
|
||||
"/authenticator/set",
|
||||
);
|
||||
});
|
||||
|
||||
it("shows an error if invite code validation failed", () => {
|
||||
stub("zitadel.user.v2.UserService", "VerifyInviteCode", {
|
||||
code: 3,
|
||||
error: "error validating code",
|
||||
});
|
||||
// TODO: Avoid uncaught exception in application
|
||||
cy.once("uncaught:exception", () => false);
|
||||
cy.visit("/verify?userId=221394658884845598&code=abc&invite=true");
|
||||
cy.contains("Could not verify invite", { timeout: 10_000 });
|
||||
});
|
||||
});
|
||||
|
||||
describe("verify email", () => {
|
||||
beforeEach(() => {
|
||||
stub("zitadel.org.v2.OrganizationService", "ListOrganizations", {
|
||||
data: {
|
||||
details: {
|
||||
totalResult: 1,
|
||||
},
|
||||
result: [{ id: "256088834543534543" }],
|
||||
},
|
||||
});
|
||||
|
||||
stub("zitadel.user.v2.UserService", "ListAuthenticationMethodTypes", {
|
||||
data: {
|
||||
authMethodTypes: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("shows password and passkey method after successful invite verification", () => {
|
||||
stub("zitadel.user.v2.UserService", "VerifyEmail");
|
||||
cy.visit("/verify?userId=221394658884845598&code=abc");
|
||||
cy.location("pathname", { timeout: 10_000 }).should(
|
||||
"eq",
|
||||
"/authenticator/set",
|
||||
);
|
||||
});
|
||||
|
||||
it("shows an error if invite code validation failed", () => {
|
||||
it("shows an error if email code validation failed", () => {
|
||||
stub("zitadel.user.v2.UserService", "VerifyEmail", {
|
||||
code: 3,
|
||||
error: "error validating code",
|
||||
|
||||
@@ -3,7 +3,7 @@ import { DynamicTheme } from "@/components/dynamic-theme";
|
||||
import { UserAvatar } from "@/components/user-avatar";
|
||||
import { VerifyForm } from "@/components/verify-form";
|
||||
import { VerifyRedirectButton } from "@/components/verify-redirect-button";
|
||||
import { sendCode } from "@/lib/server/verify";
|
||||
import { sendEmailCode } from "@/lib/server/verify";
|
||||
import { loadMostRecentSession } from "@/lib/session";
|
||||
import {
|
||||
getBrandingSettings,
|
||||
@@ -37,26 +37,28 @@ export default async function Page(props: { searchParams: Promise<any> }) {
|
||||
let human: HumanUser | undefined;
|
||||
let id: string | undefined;
|
||||
|
||||
const doSend = !skipsend && invite !== "true";
|
||||
|
||||
if ("loginName" in searchParams) {
|
||||
sessionFactors = await loadMostRecentSession({
|
||||
loginName,
|
||||
organization,
|
||||
});
|
||||
|
||||
if (!skipsend && sessionFactors?.factors?.user?.id) {
|
||||
await sendCode({
|
||||
if (doSend && sessionFactors?.factors?.user?.id) {
|
||||
await sendEmailCode({
|
||||
userId: sessionFactors?.factors?.user?.id,
|
||||
isInvite: invite === "true",
|
||||
authRequestId,
|
||||
}).catch((error) => {
|
||||
console.error("Could not resend verification email", error);
|
||||
throw Error("Could not request email");
|
||||
});
|
||||
}
|
||||
} else if ("userId" in searchParams && userId) {
|
||||
if (!skipsend) {
|
||||
await sendCode({
|
||||
if (doSend) {
|
||||
await sendEmailCode({
|
||||
userId,
|
||||
isInvite: invite === "true",
|
||||
authRequestId,
|
||||
}).catch((error) => {
|
||||
console.error("Could not resend verification email", error);
|
||||
throw Error("Could not request email");
|
||||
|
||||
@@ -171,6 +171,7 @@ export async function sendLoginname(command: SendLoginnameCommand) {
|
||||
session.factors?.user?.id,
|
||||
);
|
||||
|
||||
// this can be expected to be an invite as users created in console have a password set.
|
||||
if (!methods.authMethodTypes || !methods.authMethodTypes.length) {
|
||||
const humanUser =
|
||||
potentialUsers[0].type.case === "human"
|
||||
|
||||
@@ -7,9 +7,9 @@ import {
|
||||
listAuthenticationMethodTypes,
|
||||
resendEmailCode,
|
||||
resendInviteCode,
|
||||
sendEmailCode,
|
||||
verifyEmail,
|
||||
verifyInviteCode,
|
||||
sendEmailCode as zitadelSendEmailCode,
|
||||
} from "@/lib/zitadel";
|
||||
import { create } from "@zitadel/client";
|
||||
import { Session } from "@zitadel/proto/zitadel/session/v2/session_pb";
|
||||
@@ -192,9 +192,14 @@ export async function resendVerification(command: resendVerifyEmailCommand) {
|
||||
: resendEmailCode(command.userId, host, command.authRequestId);
|
||||
}
|
||||
|
||||
export async function sendCode(command: resendVerifyEmailCommand) {
|
||||
type sendEmailCommand = {
|
||||
userId: string;
|
||||
authRequestId?: string;
|
||||
};
|
||||
|
||||
export async function sendEmailCode(command: sendEmailCommand) {
|
||||
const host = (await headers()).get("host");
|
||||
return sendEmailCode(command.userId, host, command.authRequestId);
|
||||
return zitadelSendEmailCode(command.userId, host, command.authRequestId);
|
||||
}
|
||||
|
||||
export type SendVerificationRedirectWithoutCheckCommand = {
|
||||
|
||||
Reference in New Issue
Block a user