test: add verify email and password change required

This commit is contained in:
Stefan Benz
2025-01-09 15:49:44 +01:00
parent 11c6c9ed16
commit 3443482d2b
10 changed files with 376 additions and 380 deletions

View File

@@ -1,12 +1,12 @@
import {expect, Page} from "@playwright/test";
import { expect, Page } from "@playwright/test";
const codeTextInput = "code-text-input";
export async function emailVerifyScreen(page: Page, code: string) {
await page.getByTestId(codeTextInput).pressSequentially(code);
await page.getByTestId(codeTextInput).pressSequentially(code);
}
export async function emailVerifyScreenExpect(page: Page, code: string) {
await expect(page.getByTestId(codeTextInput)).toHaveValue(code);
await expect(page.getByTestId("error").locator("div")).toContainText("Could not verify email");
await expect(page.getByTestId(codeTextInput)).toHaveValue(code);
await expect(page.getByTestId("error").locator("div")).toContainText("Could not verify email");
}

View File

@@ -1,73 +1,73 @@
import {faker} from "@faker-js/faker";
import {test as base} from "@playwright/test";
import { faker } from "@faker-js/faker";
import { test as base } from "@playwright/test";
import dotenv from "dotenv";
import path from "path";
import {loginScreenExpect, loginWithPassword} from "./login";
import {PasswordUser} from "./user";
import {emailVerify, emailVerifyResend} from "./email-verify";
import {emailVerifyScreenExpect} from "./email-verify-screen";
import {getCodeFromSink} from "./sink"
import { emailVerify, emailVerifyResend } from "./email-verify";
import { emailVerifyScreenExpect } from "./email-verify-screen";
import { loginScreenExpect, loginWithPassword } from "./login";
import { getCodeFromSink } from "./sink";
import { PasswordUser } from "./user";
// Read from ".env" file.
dotenv.config({path: path.resolve(__dirname, ".env.local")});
dotenv.config({ path: path.resolve(__dirname, ".env.local") });
const test = base.extend<{ user: PasswordUser }>({
user: async ({page}, use) => {
const user = new PasswordUser({
email: faker.internet.email(),
isEmailVerified: false,
firstName: faker.person.firstName(),
lastName: faker.person.lastName(),
organization: "",
phone: faker.phone.number(),
isPhoneVerified: false,
password: "Password1!",
passwordChangeRequired: false,
});
await user.ensure(page);
await use(user);
await user.cleanup();
},
user: async ({ page }, use) => {
const user = new PasswordUser({
email: faker.internet.email(),
isEmailVerified: false,
firstName: faker.person.firstName(),
lastName: faker.person.lastName(),
organization: "",
phone: faker.phone.number(),
isPhoneVerified: false,
password: "Password1!",
passwordChangeRequired: false,
});
await user.ensure(page);
await use(user);
await user.cleanup();
},
});
test("user email not verified, verify", async ({user, page}) => {
await loginWithPassword(page, user.getUsername(), user.getPassword());
// auto-redirect on /verify
// wait for send of the code
await page.waitForTimeout(3000);
const c = await getCodeFromSink(user.getUsername());
await emailVerify(page, c)
await loginScreenExpect(page, user.getFullName());
test("user email not verified, verify", async ({ user, page }) => {
await loginWithPassword(page, user.getUsername(), user.getPassword());
// auto-redirect on /verify
// wait for send of the code
await page.waitForTimeout(3000);
const c = await getCodeFromSink(user.getUsername());
await emailVerify(page, c);
await loginScreenExpect(page, user.getFullName());
});
test("user email not verified, resend, verify", async ({user, page}) => {
await loginWithPassword(page, user.getUsername(), user.getPassword());
// auto-redirect on /verify
await emailVerifyResend(page);
// wait for send of the code
await page.waitForTimeout(3000);
const c = await getCodeFromSink(user.getUsername());
await emailVerify(page, c)
await loginScreenExpect(page, user.getFullName());
test("user email not verified, resend, verify", async ({ user, page }) => {
await loginWithPassword(page, user.getUsername(), user.getPassword());
// auto-redirect on /verify
await emailVerifyResend(page);
// wait for send of the code
await page.waitForTimeout(3000);
const c = await getCodeFromSink(user.getUsername());
await emailVerify(page, c);
await loginScreenExpect(page, user.getFullName());
});
test("user email not verified, resend, old code", async ({user, page}) => {
await loginWithPassword(page, user.getUsername(), user.getPassword());
// auto-redirect on /verify
// wait for send of the code
await page.waitForTimeout(3000);
const c = await getCodeFromSink(user.getUsername());
await emailVerifyResend(page);
// wait for resend of the code
await page.waitForTimeout(1000);
await emailVerify(page, c)
await emailVerifyScreenExpect(page, c);
test("user email not verified, resend, old code", async ({ user, page }) => {
await loginWithPassword(page, user.getUsername(), user.getPassword());
// auto-redirect on /verify
// wait for send of the code
await page.waitForTimeout(3000);
const c = await getCodeFromSink(user.getUsername());
await emailVerifyResend(page);
// wait for resend of the code
await page.waitForTimeout(1000);
await emailVerify(page, c);
await emailVerifyScreenExpect(page, c);
});
test("user email not verified, wrong code", async ({user, page}) => {
await loginWithPassword(page, user.getUsername(), user.getPassword());
// auto-redirect on /verify
const code = "wrong"
await emailVerify(page, code)
await emailVerifyScreenExpect(page, code);
test("user email not verified, wrong code", async ({ user, page }) => {
await loginWithPassword(page, user.getUsername(), user.getPassword());
// auto-redirect on /verify
const code = "wrong";
await emailVerify(page, code);
await emailVerifyScreenExpect(page, code);
});

View File

@@ -1,16 +1,15 @@
import { Page } from "@playwright/test";
import { emailVerifyScreen } from "./email-verify-screen";
import { getOtpFromSink } from "./sink";
export async function startEmailVerify(page: Page, loginname: string) {
await page.goto("/verify");
await page.goto("/verify");
}
export async function emailVerify(page: Page, code: string) {
await emailVerifyScreen(page, code);
await page.getByTestId("submit-button").click();
await emailVerifyScreen(page, code);
await page.getByTestId("submit-button").click();
}
export async function emailVerifyResend(page: Page) {
await page.getByTestId("resend-button").click();
await page.getByTestId("resend-button").click();
}

View File

@@ -1,5 +1,5 @@
import {expect, Page} from "@playwright/test";
import {getCodeFromSink} from "./sink";
import { expect, Page } from "@playwright/test";
import { getCodeFromSink } from "./sink";
const codeField = "code-text-input";
const passwordField = "password-text-input";
@@ -19,83 +19,83 @@ const matchText = "Matches";
const noMatchText = "Doesn't match";
export async function changePasswordScreen(page: Page, password1: string, password2: string) {
await page.getByTestId(passwordChangeField).pressSequentially(password1);
await page.getByTestId(passwordChangeConfirmField).pressSequentially(password2);
await page.getByTestId(passwordChangeField).pressSequentially(password1);
await page.getByTestId(passwordChangeConfirmField).pressSequentially(password2);
}
export async function passwordScreen(page: Page, password: string) {
await page.getByTestId(passwordField).pressSequentially(password);
await page.getByTestId(passwordField).pressSequentially(password);
}
export async function passwordScreenExpect(page: Page, password: string) {
await expect(page.getByTestId(passwordField)).toHaveValue(password);
await expect(page.getByTestId("error").locator("div")).toContainText("Could not verify password");
await expect(page.getByTestId(passwordField)).toHaveValue(password);
await expect(page.getByTestId("error").locator("div")).toContainText("Could not verify password");
}
export async function changePasswordScreenExpect(
page: Page,
password1: string,
password2: string,
length: boolean,
symbol: boolean,
number: boolean,
uppercase: boolean,
lowercase: boolean,
equals: boolean,
page: Page,
password1: string,
password2: string,
length: boolean,
symbol: boolean,
number: boolean,
uppercase: boolean,
lowercase: boolean,
equals: boolean,
) {
await expect(page.getByTestId(passwordChangeField)).toHaveValue(password1);
await expect(page.getByTestId(passwordChangeConfirmField)).toHaveValue(password2);
await expect(page.getByTestId(passwordChangeField)).toHaveValue(password1);
await expect(page.getByTestId(passwordChangeConfirmField)).toHaveValue(password2);
await checkComplexity(page, length, symbol, number, uppercase, lowercase, equals);
await checkComplexity(page, length, symbol, number, uppercase, lowercase, equals);
}
async function checkComplexity(
page: Page,
length: boolean,
symbol: boolean,
number: boolean,
uppercase: boolean,
lowercase: boolean,
equals: boolean,
page: Page,
length: boolean,
symbol: boolean,
number: boolean,
uppercase: boolean,
lowercase: boolean,
equals: boolean,
) {
await checkContent(page, lengthCheck, length);
await checkContent(page, symbolCheck, symbol);
await checkContent(page, numberCheck, number);
await checkContent(page, uppercaseCheck, uppercase);
await checkContent(page, lowercaseCheck, lowercase);
await checkContent(page, equalCheck, equals);
await checkContent(page, lengthCheck, length);
await checkContent(page, symbolCheck, symbol);
await checkContent(page, numberCheck, number);
await checkContent(page, uppercaseCheck, uppercase);
await checkContent(page, lowercaseCheck, lowercase);
await checkContent(page, equalCheck, equals);
}
async function checkContent(page: Page, testid: string, match: boolean) {
if (match) {
await expect(page.getByTestId(testid)).toContainText(matchText);
} else {
await expect(page.getByTestId(testid)).toContainText(noMatchText);
}
if (match) {
await expect(page.getByTestId(testid)).toContainText(matchText);
} else {
await expect(page.getByTestId(testid)).toContainText(noMatchText);
}
}
export async function resetPasswordScreen(page: Page, username: string, password1: string, password2: string) {
// wait for send of the code
await page.waitForTimeout(3000);
const c = await getCodeFromSink(username);
await page.getByTestId(codeField).pressSequentially(c);
await page.getByTestId(passwordSetField).pressSequentially(password1);
await page.getByTestId(passwordSetConfirmField).pressSequentially(password2);
// wait for send of the code
await page.waitForTimeout(3000);
const c = await getCodeFromSink(username);
await page.getByTestId(codeField).pressSequentially(c);
await page.getByTestId(passwordSetField).pressSequentially(password1);
await page.getByTestId(passwordSetConfirmField).pressSequentially(password2);
}
export async function resetPasswordScreenExpect(
page: Page,
password1: string,
password2: string,
length: boolean,
symbol: boolean,
number: boolean,
uppercase: boolean,
lowercase: boolean,
equals: boolean,
page: Page,
password1: string,
password2: string,
length: boolean,
symbol: boolean,
number: boolean,
uppercase: boolean,
lowercase: boolean,
equals: boolean,
) {
await expect(page.getByTestId(passwordSetField)).toHaveValue(password1);
await expect(page.getByTestId(passwordSetConfirmField)).toHaveValue(password2);
await expect(page.getByTestId(passwordSetField)).toHaveValue(password1);
await expect(page.getByTestId(passwordSetConfirmField)).toHaveValue(password2);
await checkComplexity(page, length, symbol, number, uppercase, lowercase, equals);
}
await checkComplexity(page, length, symbol, number, uppercase, lowercase, equals);
}

View File

@@ -1,43 +1,42 @@
import {Page} from "@playwright/test";
import {passkeyRegister} from "./passkey";
import {registerPasswordScreen, registerUserScreenPasskey, registerUserScreenPassword} from "./register-screen";
import {getCodeFromSink} from "./sink";
import {emailVerify} from "./email-verify";
import { Page } from "@playwright/test";
import { emailVerify } from "./email-verify";
import { passkeyRegister } from "./passkey";
import { registerPasswordScreen, registerUserScreenPasskey, registerUserScreenPassword } from "./register-screen";
import { getCodeFromSink } from "./sink";
export async function registerWithPassword(
page: Page,
firstname: string,
lastname: string,
email: string,
password1: string,
password2: string,
page: Page,
firstname: string,
lastname: string,
email: string,
password1: string,
password2: string,
) {
await page.goto("/register");
await registerUserScreenPassword(page, firstname, lastname, email);
await page.getByTestId("submit-button").click();
await registerPasswordScreen(page, password1, password2);
await page.getByTestId("submit-button").click();
await page.waitForTimeout(3000);
await page.goto("/register");
await registerUserScreenPassword(page, firstname, lastname, email);
await page.getByTestId("submit-button").click();
await registerPasswordScreen(page, password1, password2);
await page.getByTestId("submit-button").click();
await page.waitForTimeout(3000);
await verifyEmail(page, email)
await verifyEmail(page, email);
}
export async function registerWithPasskey(page: Page, firstname: string, lastname: string, email: string): Promise<string> {
await page.goto("/register");
await registerUserScreenPasskey(page, firstname, lastname, email);
await page.getByTestId("submit-button").click();
await page.goto("/register");
await registerUserScreenPasskey(page, firstname, lastname, email);
await page.getByTestId("submit-button").click();
// wait for projection of user
await page.waitForTimeout(3000);
const authId = await passkeyRegister(page);
// wait for projection of user
await page.waitForTimeout(3000);
const authId = await passkeyRegister(page);
await verifyEmail(page, email)
return authId
await verifyEmail(page, email);
return authId;
}
async function verifyEmail(page: Page, email: string) {
await page.waitForTimeout(1000);
const c = await getCodeFromSink(email);
await emailVerify(page, c)
}
await page.waitForTimeout(1000);
const c = await getCodeFromSink(email);
await emailVerify(page, c);
}

View File

@@ -2,10 +2,8 @@ import { faker } from "@faker-js/faker";
import { test as base } from "@playwright/test";
import dotenv from "dotenv";
import path from "path";
import { loginScreenExpect, loginWithPassword, startLogin } from "./login";
import { loginname } from "./loginname";
import {changePassword, resetPassword, startResetPassword} from "./password";
import { resetPasswordScreen, resetPasswordScreenExpect } from "./password-screen";
import { loginScreenExpect, loginWithPassword } from "./login";
import { changePassword } from "./password";
import { PasswordUser } from "./user";
// Read from ".env" file.

View File

@@ -1,54 +1,54 @@
import {faker} from "@faker-js/faker";
import {test as base} from "@playwright/test";
import { faker } from "@faker-js/faker";
import { test as base } from "@playwright/test";
import dotenv from "dotenv";
import path from "path";
import {loginScreenExpect, loginWithPassword} from "./login";
import {changePassword, startChangePassword} from "./password";
import {changePasswordScreen, changePasswordScreenExpect} from "./password-screen";
import {PasswordUser} from "./user";
import { loginScreenExpect, loginWithPassword } from "./login";
import { changePassword, startChangePassword } from "./password";
import { changePasswordScreen, changePasswordScreenExpect } from "./password-screen";
import { PasswordUser } from "./user";
// Read from ".env" file.
dotenv.config({path: path.resolve(__dirname, ".env.local")});
dotenv.config({ path: path.resolve(__dirname, ".env.local") });
const test = base.extend<{ user: PasswordUser }>({
user: async ({page}, use) => {
const user = new PasswordUser({
email: faker.internet.email(),
isEmailVerified: true,
firstName: faker.person.firstName(),
lastName: faker.person.lastName(),
organization: "",
phone: faker.phone.number(),
isPhoneVerified: false,
password: "Password1!",
passwordChangeRequired: false,
});
await user.ensure(page);
await use(user);
await user.cleanup();
},
user: async ({ page }, use) => {
const user = new PasswordUser({
email: faker.internet.email(),
isEmailVerified: true,
firstName: faker.person.firstName(),
lastName: faker.person.lastName(),
organization: "",
phone: faker.phone.number(),
isPhoneVerified: false,
password: "Password1!",
passwordChangeRequired: false,
});
await user.ensure(page);
await use(user);
await user.cleanup();
},
});
test("username and password changed login", async ({user, page}) => {
const changedPw = "ChangedPw1!";
await loginWithPassword(page, user.getUsername(), user.getPassword());
test("username and password changed login", async ({ user, page }) => {
const changedPw = "ChangedPw1!";
await loginWithPassword(page, user.getUsername(), user.getPassword());
// wait for projection of token
await page.waitForTimeout(2000);
// wait for projection of token
await page.waitForTimeout(2000);
await startChangePassword(page, user.getUsername());
await changePassword(page, changedPw);
await loginScreenExpect(page, user.getFullName());
await startChangePassword(page, user.getUsername());
await changePassword(page, changedPw);
await loginScreenExpect(page, user.getFullName());
await loginWithPassword(page, user.getUsername(), changedPw);
await loginScreenExpect(page, user.getFullName());
await loginWithPassword(page, user.getUsername(), changedPw);
await loginScreenExpect(page, user.getFullName());
});
test("password change not with desired complexity", async ({user, page}) => {
const changedPw1 = "change";
const changedPw2 = "chang";
await loginWithPassword(page, user.getUsername(), user.getPassword());
await startChangePassword(page, user.getUsername());
await changePasswordScreen(page, changedPw1, changedPw2);
await changePasswordScreenExpect(page, changedPw1, changedPw2, false, false, false, false, true, false);
test("password change not with desired complexity", async ({ user, page }) => {
const changedPw1 = "change";
const changedPw2 = "chang";
await loginWithPassword(page, user.getUsername(), user.getPassword());
await startChangePassword(page, user.getUsername());
await changePasswordScreen(page, changedPw1, changedPw2);
await changePasswordScreenExpect(page, changedPw1, changedPw2, false, false, false, false, true, false);
});

View File

@@ -4,8 +4,8 @@ import dotenv from "dotenv";
import path from "path";
import { loginScreenExpect, loginWithPassword, startLogin } from "./login";
import { loginname } from "./loginname";
import {changePassword, resetPassword, startResetPassword} from "./password";
import {changePasswordScreen, resetPasswordScreen, resetPasswordScreenExpect} from "./password-screen";
import { resetPassword, startResetPassword } from "./password";
import { resetPasswordScreen, resetPasswordScreenExpect } from "./password-screen";
import { PasswordUser } from "./user";
// Read from ".env" file.

View File

@@ -1,70 +1,70 @@
import {faker} from "@faker-js/faker";
import {test as base} from "@playwright/test";
import { faker } from "@faker-js/faker";
import { test as base } from "@playwright/test";
import dotenv from "dotenv";
import path from "path";
import {code} from "./code";
import {codeScreenExpect} from "./code-screen";
import {loginScreenExpect, loginWithPassword, loginWithPasswordAndTOTP} from "./login";
import {PasswordUserWithTOTP} from "./user";
import { code } from "./code";
import { codeScreenExpect } from "./code-screen";
import { loginScreenExpect, loginWithPassword, loginWithPasswordAndTOTP } from "./login";
import { PasswordUserWithTOTP } from "./user";
// Read from ".env" file.
dotenv.config({path: path.resolve(__dirname, ".env.local")});
dotenv.config({ path: path.resolve(__dirname, ".env.local") });
const test = base.extend<{ user: PasswordUserWithTOTP; sink: any }>({
user: async ({page}, use) => {
const user = new PasswordUserWithTOTP({
email: faker.internet.email(),
isEmailVerified: true,
firstName: faker.person.firstName(),
lastName: faker.person.lastName(),
organization: "",
phone: faker.phone.number({style: "international"}),
isPhoneVerified: true,
password: "Password1!",
passwordChangeRequired: false,
});
user: async ({ page }, use) => {
const user = new PasswordUserWithTOTP({
email: faker.internet.email(),
isEmailVerified: true,
firstName: faker.person.firstName(),
lastName: faker.person.lastName(),
organization: "",
phone: faker.phone.number({ style: "international" }),
isPhoneVerified: true,
password: "Password1!",
passwordChangeRequired: false,
});
await user.ensure(page);
await use(user);
await user.cleanup();
},
await user.ensure(page);
await use(user);
await user.cleanup();
},
});
test("username, password and totp login", async ({user, page}) => {
// Given totp is enabled on the organization of the user
// Given the user has only totp configured as second factor
// User enters username
// User enters password
// Screen for entering the code is shown directly
// User enters the code into the ui
// User is redirected to the app (default redirect url)
await loginWithPasswordAndTOTP(page, user.getUsername(), user.getPassword(), user.getSecret());
await loginScreenExpect(page, user.getFullName());
test("username, password and totp login", async ({ user, page }) => {
// Given totp is enabled on the organization of the user
// Given the user has only totp configured as second factor
// User enters username
// User enters password
// Screen for entering the code is shown directly
// User enters the code into the ui
// User is redirected to the app (default redirect url)
await loginWithPasswordAndTOTP(page, user.getUsername(), user.getPassword(), user.getSecret());
await loginScreenExpect(page, user.getFullName());
});
test("username, password and totp otp login, wrong code", async ({user, page}) => {
// Given totp is enabled on the organization of the user
// Given the user has only totp configured as second factor
// User enters username
// User enters password
// Screen for entering the code is shown directly
// User enters a wrond code
// Error message - "Invalid code" is shown
const c = "wrongcode";
await loginWithPassword(page, user.getUsername(), user.getPassword());
await code(page, c);
await codeScreenExpect(page, c);
test("username, password and totp otp login, wrong code", async ({ user, page }) => {
// Given totp is enabled on the organization of the user
// Given the user has only totp configured as second factor
// User enters username
// User enters password
// Screen for entering the code is shown directly
// User enters a wrond code
// Error message - "Invalid code" is shown
const c = "wrongcode";
await loginWithPassword(page, user.getUsername(), user.getPassword());
await code(page, c);
await codeScreenExpect(page, c);
});
test("username, password and totp login, multiple mfa options", async ({page}) => {
// Given totp and email otp is enabled on the organization of the user
// Given the user has totp and email otp configured as second factor
// User enters username
// User enters password
// Screen for entering the code is shown directly
// Button to switch to email otp is shown
// User clicks button to use email otp instead
// User receives an email with a verification code
// User enters code in ui
// User is redirected to the app (default redirect url)
test("username, password and totp login, multiple mfa options", async ({ page }) => {
// Given totp and email otp is enabled on the organization of the user
// Given the user has totp and email otp configured as second factor
// User enters username
// User enters password
// Screen for entering the code is shown directly
// Button to switch to email otp is shown
// User clicks button to use email otp instead
// User receives an email with a verification code
// User enters code in ui
// User is redirected to the app (default redirect url)
});

View File

@@ -1,166 +1,166 @@
import {Authenticator} from "@otplib/core";
import {createDigest, createRandomBytes} from "@otplib/plugin-crypto";
import {keyDecoder, keyEncoder} from "@otplib/plugin-thirty-two"; // use your chosen base32 plugin
import { Authenticator } from "@otplib/core";
import { createDigest, createRandomBytes } from "@otplib/plugin-crypto";
import { keyDecoder, keyEncoder } from "@otplib/plugin-thirty-two"; // use your chosen base32 plugin
import axios from "axios";
import {OtpType, userProps} from "./user";
import { OtpType, userProps } from "./user";
export async function addUser(props: userProps) {
const body = {
username: props.email,
organization: {
orgId: props.organization,
},
profile: {
givenName: props.firstName,
familyName: props.lastName,
},
email: {
email: props.email,
isVerified: true,
},
phone: {
phone: props.phone,
isVerified: true,
},
password: {
password: props.password,
changeRequired: props.passwordChangeRequired ?? false,
},
};
if (!props.isEmailVerified) {
delete body.email.isVerified;
}
if (!props.isPhoneVerified) {
delete body.phone.isVerified;
}
const body = {
username: props.email,
organization: {
orgId: props.organization,
},
profile: {
givenName: props.firstName,
familyName: props.lastName,
},
email: {
email: props.email,
isVerified: true,
},
phone: {
phone: props.phone,
isVerified: true,
},
password: {
password: props.password,
changeRequired: props.passwordChangeRequired ?? false,
},
};
if (!props.isEmailVerified) {
delete body.email.isVerified;
}
if (!props.isPhoneVerified) {
delete body.phone.isVerified;
}
return await listCall(`${process.env.ZITADEL_API_URL}/v2/users/human`, body);
return await listCall(`${process.env.ZITADEL_API_URL}/v2/users/human`, body);
}
export async function removeUserByUsername(username: string) {
const resp = await getUserByUsername(username);
if (!resp || !resp.result || !resp.result[0]) {
return;
}
await removeUser(resp.result[0].userId);
const resp = await getUserByUsername(username);
if (!resp || !resp.result || !resp.result[0]) {
return;
}
await removeUser(resp.result[0].userId);
}
export async function removeUser(id: string) {
await deleteCall(`${process.env.ZITADEL_API_URL}/v2/users/${id}`);
await deleteCall(`${process.env.ZITADEL_API_URL}/v2/users/${id}`);
}
async function deleteCall(url: string) {
try {
const response = await axios.delete(url, {
headers: {
Authorization: `Bearer ${process.env.ZITADEL_SERVICE_USER_TOKEN}`,
},
});
try {
const response = await axios.delete(url, {
headers: {
Authorization: `Bearer ${process.env.ZITADEL_SERVICE_USER_TOKEN}`,
},
});
if (response.status >= 400 && response.status !== 404) {
const error = `HTTP Error: ${response.status} - ${response.statusText}`;
console.error(error);
throw new Error(error);
}
} catch (error) {
console.error("Error making request:", error);
throw error;
if (response.status >= 400 && response.status !== 404) {
const error = `HTTP Error: ${response.status} - ${response.statusText}`;
console.error(error);
throw new Error(error);
}
} catch (error) {
console.error("Error making request:", error);
throw error;
}
}
export async function getUserByUsername(username: string): Promise<any> {
const listUsersBody = {
queries: [
{
userNameQuery: {
userName: username,
},
},
],
};
const listUsersBody = {
queries: [
{
userNameQuery: {
userName: username,
},
},
],
};
return await listCall(`${process.env.ZITADEL_API_URL}/v2/users`, listUsersBody);
return await listCall(`${process.env.ZITADEL_API_URL}/v2/users`, listUsersBody);
}
async function listCall(url: string, data: any): Promise<any> {
try {
const response = await axios.post(url, data, {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.ZITADEL_SERVICE_USER_TOKEN}`,
},
});
try {
const response = await axios.post(url, data, {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.ZITADEL_SERVICE_USER_TOKEN}`,
},
});
if (response.status >= 400) {
const error = `HTTP Error: ${response.status} - ${response.statusText}`;
console.error(error);
throw new Error(error);
}
return response.data;
} catch (error) {
console.error("Error making request:", error);
throw error;
if (response.status >= 400) {
const error = `HTTP Error: ${response.status} - ${response.statusText}`;
console.error(error);
throw new Error(error);
}
return response.data;
} catch (error) {
console.error("Error making request:", error);
throw error;
}
}
export async function activateOTP(userId: string, type: OtpType) {
let url = "otp_";
switch (type) {
case OtpType.sms:
url = url + "sms";
break;
case OtpType.email:
url = url + "email";
break;
}
let url = "otp_";
switch (type) {
case OtpType.sms:
url = url + "sms";
break;
case OtpType.email:
url = url + "email";
break;
}
await pushCall(`${process.env.ZITADEL_API_URL}/v2/users/${userId}/${url}`, {});
await pushCall(`${process.env.ZITADEL_API_URL}/v2/users/${userId}/${url}`, {});
}
async function pushCall(url: string, data: any) {
try {
const response = await axios.post(url, data, {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.ZITADEL_SERVICE_USER_TOKEN}`,
},
});
try {
const response = await axios.post(url, data, {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.ZITADEL_SERVICE_USER_TOKEN}`,
},
});
if (response.status >= 400) {
const error = `HTTP Error: ${response.status} - ${response.statusText}`;
console.error(error);
throw new Error(error);
}
} catch (error) {
console.error("Error making request:", error);
throw error;
if (response.status >= 400) {
const error = `HTTP Error: ${response.status} - ${response.statusText}`;
console.error(error);
throw new Error(error);
}
} catch (error) {
console.error("Error making request:", error);
throw error;
}
}
export async function addTOTP(userId: string): Promise<string> {
const response = await listCall(`${process.env.ZITADEL_API_URL}/v2/users/${userId}/totp`, {});
const code = totp(response.secret);
await pushCall(`${process.env.ZITADEL_API_URL}/v2/users/${userId}/totp/verify`, {code: code});
return response.secret;
const response = await listCall(`${process.env.ZITADEL_API_URL}/v2/users/${userId}/totp`, {});
const code = totp(response.secret);
await pushCall(`${process.env.ZITADEL_API_URL}/v2/users/${userId}/totp/verify`, { code: code });
return response.secret;
}
export function totp(secret: string) {
const authenticator = new Authenticator({
createDigest,
createRandomBytes,
keyDecoder,
keyEncoder,
});
// google authenticator usage
const token = authenticator.generate(secret);
const authenticator = new Authenticator({
createDigest,
createRandomBytes,
keyDecoder,
keyEncoder,
});
// google authenticator usage
const token = authenticator.generate(secret);
// check if token can be used
if (!authenticator.verify({token: token, secret: secret})) {
const error = `Generated token could not be verified`;
console.error(error);
throw new Error(error);
}
// check if token can be used
if (!authenticator.verify({ token: token, secret: secret })) {
const error = `Generated token could not be verified`;
console.error(error);
throw new Error(error);
}
return token;
return token;
}