mirror of
https://github.com/zitadel/zitadel.git
synced 2025-12-11 21:52:32 +00:00
refactor: zitadel server
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
This commit is contained in:
37
README.md
37
README.md
@@ -1,14 +1,17 @@
|
|||||||
# ZITADEL TypeScript with Turborepo and Changesets
|
# ZITADEL TypeScript with Turborepo and Changesets
|
||||||
|
|
||||||
This repository contains all TypeScript and JavaScript packages and applications you need to create your own ZITADEL Login UI.
|
This repository contains all TypeScript and JavaScript packages and applications you need to create your own ZITADEL
|
||||||
The repo makes use of the [build system Turbo](https://turbo.build/repo) and the [Changesets CLI for versioning the packages](https://github.com/changesets/changesets).
|
Login UI.
|
||||||
|
The repo makes use of the [build system Turbo](https://turbo.build/repo) and
|
||||||
|
the [Changesets CLI for versioning the packages](https://github.com/changesets/changesets).
|
||||||
|
|
||||||
**⚠️ This repo and packages are in alpha state and subject to change ⚠️**
|
**⚠️ This repo and packages are in alpha state and subject to change ⚠️**
|
||||||
|
|
||||||
The scope of functionality of this repo and packages is limited and under active development.
|
The scope of functionality of this repo and packages is limited and under active development.
|
||||||
Once the package structure is set and all APIs are fully implemented we'll move this repo to beta state.
|
Once the package structure is set and all APIs are fully implemented we'll move this repo to beta state.
|
||||||
You can read the [contribution guide](/CONTRIBUTING.md) on how to contribute.
|
You can read the [contribution guide](/CONTRIBUTING.md) on how to contribute.
|
||||||
Questions can be raised in our [Discord channel](https://discord.gg/erh5Brh7jE) or as a [GitHub issue](https://github.com/zitadel/typescript/issues).
|
Questions can be raised in our [Discord channel](https://discord.gg/erh5Brh7jE) or as
|
||||||
|
a [GitHub issue](https://github.com/zitadel/typescript/issues).
|
||||||
|
|
||||||
## Developing Your Own ZITADEL Login UI
|
## Developing Your Own ZITADEL Login UI
|
||||||
|
|
||||||
@@ -25,8 +28,8 @@ We think the easiest path of getting up and running, is the following:
|
|||||||
## Included Apps And Packages
|
## Included Apps And Packages
|
||||||
|
|
||||||
- `login`: The login UI used by ZITADEL Cloud, powered by Next.js
|
- `login`: The login UI used by ZITADEL Cloud, powered by Next.js
|
||||||
- `@zitadel/server`: core components for establishing node client connection, grpc stub
|
- `@zitadel/node`: core components for establishing node client connection, grpc stub
|
||||||
- `@zitadel/client`: core components for establishing web client connection, grpc stub
|
- `@zitadel/client`: shared client utilities
|
||||||
- `@zitadel/proto`: shared protobuf types
|
- `@zitadel/proto`: shared protobuf types
|
||||||
- `@zitadel/react`: shared React utilities and components built with tailwindcss
|
- `@zitadel/react`: shared React utilities and components built with tailwindcss
|
||||||
- `@zitadel/next`: shared Next.js utilities
|
- `@zitadel/next`: shared Next.js utilities
|
||||||
@@ -38,9 +41,11 @@ Each package and app is 100% [TypeScript](https://www.typescriptlang.org/).
|
|||||||
### Login
|
### Login
|
||||||
|
|
||||||
The login is currently in a work in progress state.
|
The login is currently in a work in progress state.
|
||||||
The goal is to implement a login UI, using the session API of ZITADEL, which also implements the OIDC Standard and is ready to use for everyone.
|
The goal is to implement a login UI, using the session API of ZITADEL, which also implements the OIDC Standard and is
|
||||||
|
ready to use for everyone.
|
||||||
|
|
||||||
In the first phase we want to have a MVP login ready with the OIDC Standard and a basic feature set. In a second step the features will be extended.
|
In the first phase we want to have a MVP login ready with the OIDC Standard and a basic feature set. In a second step
|
||||||
|
the features will be extended.
|
||||||
|
|
||||||
This list should show the current implementation state, and also what is missing.
|
This list should show the current implementation state, and also what is missing.
|
||||||
You can already use the current state, and extend it with your needs.
|
You can already use the current state, and extend it with your needs.
|
||||||
@@ -105,11 +110,14 @@ You can already use the current state, and extend it with your needs.
|
|||||||
## Versioning And Publishing Packages
|
## Versioning And Publishing Packages
|
||||||
|
|
||||||
Package publishing has been configured using [Changesets](https://github.com/changesets/changesets).
|
Package publishing has been configured using [Changesets](https://github.com/changesets/changesets).
|
||||||
Here is their [documentation](https://github.com/changesets/changesets#documentation) for more information about the workflow.
|
Here is their [documentation](https://github.com/changesets/changesets#documentation) for more information about the
|
||||||
|
workflow.
|
||||||
|
|
||||||
The [GitHub Action](https://github.com/changesets/action) needs an `NPM_TOKEN` and `GITHUB_TOKEN` in the repository settings. The [Changesets bot](https://github.com/apps/changeset-bot) should also be installed on the GitHub repository.
|
The [GitHub Action](https://github.com/changesets/action) needs an `NPM_TOKEN` and `GITHUB_TOKEN` in the repository
|
||||||
|
settings. The [Changesets bot](https://github.com/apps/changeset-bot) should also be installed on the GitHub repository.
|
||||||
|
|
||||||
Read the [changesets documentation](https://github.com/changesets/changesets/blob/main/docs/automating-changesets.md) for more information about this automation
|
Read the [changesets documentation](https://github.com/changesets/changesets/blob/main/docs/automating-changesets.md)
|
||||||
|
for more information about this automation
|
||||||
|
|
||||||
### NPM
|
### NPM
|
||||||
|
|
||||||
@@ -136,8 +144,10 @@ pnpm install
|
|||||||
```
|
```
|
||||||
|
|
||||||
then setup the environment for the login application which needs a `.env.local` in `/apps/login`.
|
then setup the environment for the login application which needs a `.env.local` in `/apps/login`.
|
||||||
Go to your instance and create a service user for the application having the IAM_OWNER manager role.
|
Go to your instance and create a service user for the application having the `IAM_OWNER` manager role.
|
||||||
This user is required to have access to create users on your primary organization and reading policy data so it can be restricted to your personal use case but we'll stick with IAM_OWNER for convenience. Create a PAT and copy the value to paste it under the `ZITADEL_SERVICE_USER_TOKEN` key.
|
This user is required to have access to create users on your primary organization and reading policy data so it can be
|
||||||
|
restricted to your personal use case but we'll stick with `IAM_OWNER` for convenience. Create a PAT and copy the value to
|
||||||
|
paste it under the `ZITADEL_SERVICE_USER_TOKEN` key.
|
||||||
The file should look as follows:
|
The file should look as follows:
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -164,7 +174,8 @@ Open the login application with your favorite browser at `localhost:3000`.
|
|||||||
|
|
||||||
To deploy your own version on Vercel, navigate to your instance and create a service user.
|
To deploy your own version on Vercel, navigate to your instance and create a service user.
|
||||||
Copy its id from the overview and set it as ZITADEL_SERVICE_USER_ID.
|
Copy its id from the overview and set it as ZITADEL_SERVICE_USER_ID.
|
||||||
Then create a personal access token (PAT), copy and set it as ZITADEL_SERVICE_USER_TOKEN, then navigate to your instance settings and make sure it gets IAM_OWNER permissions.
|
Then create a personal access token (PAT), copy and set it as ZITADEL_SERVICE_USER_TOKEN, then navigate to your instance
|
||||||
|
settings and make sure it gets IAM_OWNER permissions.
|
||||||
Finally set your instance url as ZITADEL_API_URL. Make sure to set it without trailing slash.
|
Finally set your instance url as ZITADEL_API_URL. Make sure to set it without trailing slash.
|
||||||
|
|
||||||
[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fzitadel%2Ftypescript&env=ZITADEL_API_URL,ZITADEL_SERVICE_USER_ID,ZITADEL_SERVICE_USER_TOKEN&root-directory=apps/login&envDescription=Setup%20a%20service%20account%20with%20IAM_OWNER%20membership%20on%20your%20instance%20and%20provide%20its%20id%20and%20personal%20access%20token.&project-name=zitadel-login&repository-name=zitadel-login)
|
[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fzitadel%2Ftypescript&env=ZITADEL_API_URL,ZITADEL_SERVICE_USER_ID,ZITADEL_SERVICE_USER_TOKEN&root-directory=apps/login&envDescription=Setup%20a%20service%20account%20with%20IAM_OWNER%20membership%20on%20your%20instance%20and%20provide%20its%20id%20and%20personal%20access%20token.&project-name=zitadel-login&repository-name=zitadel-login)
|
||||||
|
|||||||
@@ -32,13 +32,14 @@
|
|||||||
"*": "prettier --write --ignore-unknown"
|
"*": "prettier --write --ignore-unknown"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@headlessui/react": "^1.7.14",
|
"@headlessui/react": "^1.7.18",
|
||||||
"@heroicons/react": "2.0.13",
|
"@heroicons/react": "2.1.3",
|
||||||
"@tailwindcss/forms": "0.5.3",
|
"@tailwindcss/forms": "0.5.7",
|
||||||
"@vercel/analytics": "^1.0.0",
|
"@vercel/analytics": "^1.2.2",
|
||||||
"@zitadel/client": "workspace:*",
|
"@zitadel/proto": "workspace:*",
|
||||||
|
"@zitadel/client2": "workspace:*",
|
||||||
"@zitadel/react": "workspace:*",
|
"@zitadel/react": "workspace:*",
|
||||||
"@zitadel/server": "workspace:*",
|
"@zitadel/node": "workspace:*",
|
||||||
"clsx": "1.2.1",
|
"clsx": "1.2.1",
|
||||||
"copy-to-clipboard": "^3.3.3",
|
"copy-to-clipboard": "^3.3.3",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
|
|||||||
@@ -1,17 +1,15 @@
|
|||||||
import { Session } from "@zitadel/server";
|
import { getBrandingSettings, listSessions } from "@/lib/zitadel";
|
||||||
import { getBrandingSettings, listSessions, server } from "@/lib/zitadel";
|
|
||||||
import { getAllSessionCookieIds } from "@/utils/cookies";
|
import { getAllSessionCookieIds } from "@/utils/cookies";
|
||||||
import { UserPlusIcon } from "@heroicons/react/24/outline";
|
import { UserPlusIcon } from "@heroicons/react/24/outline";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import SessionsList from "@/ui/SessionsList";
|
import SessionsList from "@/ui/SessionsList";
|
||||||
import DynamicTheme from "@/ui/DynamicTheme";
|
import DynamicTheme from "@/ui/DynamicTheme";
|
||||||
|
|
||||||
async function loadSessions(): Promise<Session[]> {
|
async function loadSessions() {
|
||||||
const ids = await getAllSessionCookieIds();
|
const ids = await getAllSessionCookieIds();
|
||||||
|
|
||||||
if (ids && ids.length) {
|
if (ids && ids.length) {
|
||||||
const response = await listSessions(
|
const response = await listSessions(
|
||||||
server,
|
|
||||||
ids.filter((id: string | undefined) => !!id),
|
ids.filter((id: string | undefined) => !!id),
|
||||||
);
|
);
|
||||||
return response?.sessions ?? [];
|
return response?.sessions ?? [];
|
||||||
@@ -31,7 +29,7 @@ export default async function Page({
|
|||||||
|
|
||||||
let sessions = await loadSessions();
|
let sessions = await loadSessions();
|
||||||
|
|
||||||
const branding = await getBrandingSettings(server, organization);
|
const branding = await getBrandingSettings(organization);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DynamicTheme branding={branding}>
|
<DynamicTheme branding={branding}>
|
||||||
|
|||||||
@@ -1,16 +1,6 @@
|
|||||||
import { ProviderSlug } from "@/lib/demos";
|
import { ProviderSlug } from "@/lib/demos";
|
||||||
import { getBrandingSettings, server } from "@/lib/zitadel";
|
import { getBrandingSettings } from "@/lib/zitadel";
|
||||||
import Alert, { AlertType } from "@/ui/Alert";
|
|
||||||
import DynamicTheme from "@/ui/DynamicTheme";
|
import DynamicTheme from "@/ui/DynamicTheme";
|
||||||
import IdpSignin from "@/ui/IdpSignin";
|
|
||||||
import {
|
|
||||||
AddHumanUserRequest,
|
|
||||||
IDPInformation,
|
|
||||||
RetrieveIdentityProviderIntentResponse,
|
|
||||||
user,
|
|
||||||
IDPLink,
|
|
||||||
} from "@zitadel/server";
|
|
||||||
import { ClientError } from "nice-grpc";
|
|
||||||
|
|
||||||
const PROVIDER_NAME_MAPPING: {
|
const PROVIDER_NAME_MAPPING: {
|
||||||
[provider: string]: string;
|
[provider: string]: string;
|
||||||
@@ -29,7 +19,7 @@ export default async function Page({
|
|||||||
const { id, token, authRequestId, organization } = searchParams;
|
const { id, token, authRequestId, organization } = searchParams;
|
||||||
const { provider } = params;
|
const { provider } = params;
|
||||||
|
|
||||||
const branding = await getBrandingSettings(server, organization);
|
const branding = await getBrandingSettings(organization);
|
||||||
|
|
||||||
if (provider) {
|
if (provider) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,59 +1,73 @@
|
|||||||
import { ProviderSlug } from "@/lib/demos";
|
import { ProviderSlug } from "@/lib/demos";
|
||||||
import { getBrandingSettings, server } from "@/lib/zitadel";
|
import { getBrandingSettings, userService } from "@/lib/zitadel";
|
||||||
import Alert, { AlertType } from "@/ui/Alert";
|
import Alert, { AlertType } from "@/ui/Alert";
|
||||||
import DynamicTheme from "@/ui/DynamicTheme";
|
import DynamicTheme from "@/ui/DynamicTheme";
|
||||||
import IdpSignin from "@/ui/IdpSignin";
|
import IdpSignin from "@/ui/IdpSignin";
|
||||||
|
import { AddHumanUserRequest } from "@zitadel/proto/zitadel/user/v2beta/user_service_pb";
|
||||||
import {
|
import {
|
||||||
AddHumanUserRequest,
|
|
||||||
IDPInformation,
|
IDPInformation,
|
||||||
RetrieveIdentityProviderIntentResponse,
|
|
||||||
user,
|
|
||||||
IDPLink,
|
IDPLink,
|
||||||
} from "@zitadel/server";
|
} from "@zitadel/proto/zitadel/user/v2beta/idp_pb";
|
||||||
import { ClientError } from "nice-grpc";
|
import { PartialMessage } from "@zitadel/client2";
|
||||||
|
|
||||||
const PROVIDER_MAPPING: {
|
const PROVIDER_MAPPING: {
|
||||||
[provider: string]: (rI: IDPInformation) => Partial<AddHumanUserRequest>;
|
[provider: string]: (
|
||||||
|
rI: IDPInformation,
|
||||||
|
) => PartialMessage<AddHumanUserRequest>;
|
||||||
} = {
|
} = {
|
||||||
[ProviderSlug.GOOGLE]: (idp: IDPInformation) => {
|
[ProviderSlug.GOOGLE]: (idp: IDPInformation) => {
|
||||||
const idpLink: IDPLink = {
|
const rawInfo = idp.rawInformation?.toJson() as {
|
||||||
|
User: {
|
||||||
|
email: string;
|
||||||
|
name?: string;
|
||||||
|
given_name?: string;
|
||||||
|
family_name?: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const idpLink: PartialMessage<IDPLink> = {
|
||||||
idpId: idp.idpId,
|
idpId: idp.idpId,
|
||||||
userId: idp.userId,
|
userId: idp.userId,
|
||||||
userName: idp.userName,
|
userName: idp.userName,
|
||||||
};
|
};
|
||||||
const req: Partial<AddHumanUserRequest> = {
|
|
||||||
|
const req: PartialMessage<AddHumanUserRequest> = {
|
||||||
username: idp.userName,
|
username: idp.userName,
|
||||||
email: {
|
email: {
|
||||||
email: idp.rawInformation?.User?.email,
|
email: rawInfo.User?.email,
|
||||||
isVerified: true,
|
verification: { case: "isVerified", value: true },
|
||||||
},
|
},
|
||||||
// organisation: Organisation | undefined;
|
// organisation: Organisation | undefined;
|
||||||
profile: {
|
profile: {
|
||||||
displayName: idp.rawInformation?.User?.name ?? "",
|
displayName: rawInfo.User?.name ?? "",
|
||||||
givenName: idp.rawInformation?.User?.given_name ?? "",
|
givenName: rawInfo.User?.given_name ?? "",
|
||||||
familyName: idp.rawInformation?.User?.family_name ?? "",
|
familyName: rawInfo.User?.family_name ?? "",
|
||||||
},
|
},
|
||||||
idpLinks: [idpLink],
|
idpLinks: [idpLink],
|
||||||
};
|
};
|
||||||
return req;
|
return req;
|
||||||
},
|
},
|
||||||
[ProviderSlug.GITHUB]: (idp: IDPInformation) => {
|
[ProviderSlug.GITHUB]: (idp: IDPInformation) => {
|
||||||
const idpLink: IDPLink = {
|
const rawInfo = idp.rawInformation?.toJson() as {
|
||||||
|
email: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
const idpLink: PartialMessage<IDPLink> = {
|
||||||
idpId: idp.idpId,
|
idpId: idp.idpId,
|
||||||
userId: idp.userId,
|
userId: idp.userId,
|
||||||
userName: idp.userName,
|
userName: idp.userName,
|
||||||
};
|
};
|
||||||
const req: Partial<AddHumanUserRequest> = {
|
const req: PartialMessage<AddHumanUserRequest> = {
|
||||||
username: idp.userName,
|
username: idp.userName,
|
||||||
email: {
|
email: {
|
||||||
email: idp.rawInformation?.email,
|
email: rawInfo?.email,
|
||||||
isVerified: true,
|
verification: { case: "isVerified", value: true },
|
||||||
},
|
},
|
||||||
// organisation: Organisation | undefined;
|
// organisation: Organisation | undefined;
|
||||||
profile: {
|
profile: {
|
||||||
displayName: idp.rawInformation?.name ?? "",
|
displayName: rawInfo?.name ?? "",
|
||||||
givenName: idp.rawInformation?.name ?? "",
|
givenName: rawInfo?.name ?? "",
|
||||||
familyName: idp.rawInformation?.name ?? "",
|
familyName: rawInfo?.name ?? "",
|
||||||
},
|
},
|
||||||
idpLinks: [idpLink],
|
idpLinks: [idpLink],
|
||||||
};
|
};
|
||||||
@@ -61,11 +75,7 @@ const PROVIDER_MAPPING: {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
function retrieveIDPIntent(
|
function retrieveIDPIntent(id: string, token: string) {
|
||||||
id: string,
|
|
||||||
token: string,
|
|
||||||
): Promise<RetrieveIdentityProviderIntentResponse> {
|
|
||||||
const userService = user.getUser(server);
|
|
||||||
return userService.retrieveIdentityProviderIntent(
|
return userService.retrieveIdentityProviderIntent(
|
||||||
{ idpIntentId: id, idpIntentToken: token },
|
{ idpIntentId: id, idpIntentToken: token },
|
||||||
{},
|
{},
|
||||||
@@ -77,7 +87,6 @@ function createUser(
|
|||||||
info: IDPInformation,
|
info: IDPInformation,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const userData = PROVIDER_MAPPING[provider](info);
|
const userData = PROVIDER_MAPPING[provider](info);
|
||||||
const userService = user.getUser(server);
|
|
||||||
return userService.addHumanUser(userData, {}).then((resp) => resp.userId);
|
return userService.addHumanUser(userData, {}).then((resp) => resp.userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,7 +100,7 @@ export default async function Page({
|
|||||||
const { id, token, authRequestId, organization } = searchParams;
|
const { id, token, authRequestId, organization } = searchParams;
|
||||||
const { provider } = params;
|
const { provider } = params;
|
||||||
|
|
||||||
const branding = await getBrandingSettings(server, organization);
|
const branding = await getBrandingSettings(organization);
|
||||||
|
|
||||||
if (provider && id && token) {
|
if (provider && id && token) {
|
||||||
return retrieveIDPIntent(id, token)
|
return retrieveIDPIntent(id, token)
|
||||||
@@ -128,7 +137,7 @@ export default async function Page({
|
|||||||
</DynamicTheme>
|
</DynamicTheme>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
.catch((error: ClientError) => {
|
.catch((error) => {
|
||||||
return (
|
return (
|
||||||
<DynamicTheme branding={branding}>
|
<DynamicTheme branding={branding}>
|
||||||
<div className="flex flex-col items-center space-y-4">
|
<div className="flex flex-col items-center space-y-4">
|
||||||
@@ -136,7 +145,7 @@ export default async function Page({
|
|||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
{
|
{
|
||||||
<Alert type={AlertType.ALERT}>
|
<Alert type={AlertType.ALERT}>
|
||||||
{JSON.stringify(error.details)}
|
{JSON.stringify(error.message)}
|
||||||
</Alert>
|
</Alert>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,28 +1,16 @@
|
|||||||
import {
|
import {
|
||||||
getBrandingSettings,
|
getBrandingSettings,
|
||||||
getLegalAndSupportSettings,
|
getLegalAndSupportSettings,
|
||||||
server,
|
settingsService,
|
||||||
} from "@/lib/zitadel";
|
} from "@/lib/zitadel";
|
||||||
import DynamicTheme from "@/ui/DynamicTheme";
|
import DynamicTheme from "@/ui/DynamicTheme";
|
||||||
import { SignInWithIDP } from "@/ui/SignInWithIDP";
|
import { SignInWithIDP } from "@/ui/SignInWithIDP";
|
||||||
import {
|
import { makeReqCtx } from "@zitadel/client2/v2beta";
|
||||||
GetActiveIdentityProvidersResponse,
|
|
||||||
IdentityProvider,
|
|
||||||
ZitadelServer,
|
|
||||||
settings,
|
|
||||||
} from "@zitadel/server";
|
|
||||||
|
|
||||||
function getIdentityProviders(
|
function getIdentityProviders(orgId?: string) {
|
||||||
server: ZitadelServer,
|
|
||||||
orgId?: string,
|
|
||||||
): Promise<IdentityProvider[] | undefined> {
|
|
||||||
const settingsService = settings.getSettings(server);
|
|
||||||
return settingsService
|
return settingsService
|
||||||
.getActiveIdentityProviders(
|
.getActiveIdentityProviders({ ctx: makeReqCtx(orgId) }, {})
|
||||||
orgId ? { ctx: { orgId } } : { ctx: { instance: true } },
|
.then((resp) => {
|
||||||
{},
|
|
||||||
)
|
|
||||||
.then((resp: GetActiveIdentityProvidersResponse) => {
|
|
||||||
return resp.identityProviders;
|
return resp.identityProviders;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -35,15 +23,15 @@ export default async function Page({
|
|||||||
const authRequestId = searchParams?.authRequestId;
|
const authRequestId = searchParams?.authRequestId;
|
||||||
const organization = searchParams?.organization;
|
const organization = searchParams?.organization;
|
||||||
|
|
||||||
const legal = await getLegalAndSupportSettings(server, organization);
|
const legal = await getLegalAndSupportSettings(organization);
|
||||||
|
|
||||||
const identityProviders = await getIdentityProviders(server, organization);
|
const identityProviders = await getIdentityProviders(organization);
|
||||||
|
|
||||||
const host = process.env.VERCEL_URL
|
const host = process.env.VERCEL_URL
|
||||||
? `https://${process.env.VERCEL_URL}`
|
? `https://${process.env.VERCEL_URL}`
|
||||||
: "http://localhost:3000";
|
: "http://localhost:3000";
|
||||||
|
|
||||||
const branding = await getBrandingSettings(server, organization);
|
const branding = await getBrandingSettings(organization);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DynamicTheme branding={branding}>
|
<DynamicTheme branding={branding}>
|
||||||
|
|||||||
@@ -2,29 +2,17 @@ import {
|
|||||||
getBrandingSettings,
|
getBrandingSettings,
|
||||||
getLegalAndSupportSettings,
|
getLegalAndSupportSettings,
|
||||||
getLoginSettings,
|
getLoginSettings,
|
||||||
server,
|
settingsService,
|
||||||
} from "@/lib/zitadel";
|
} from "@/lib/zitadel";
|
||||||
import DynamicTheme from "@/ui/DynamicTheme";
|
import DynamicTheme from "@/ui/DynamicTheme";
|
||||||
import { SignInWithIDP } from "@/ui/SignInWithIDP";
|
import { SignInWithIDP } from "@/ui/SignInWithIDP";
|
||||||
import UsernameForm from "@/ui/UsernameForm";
|
import UsernameForm from "@/ui/UsernameForm";
|
||||||
import {
|
import { makeReqCtx } from "@zitadel/client2/v2beta";
|
||||||
GetActiveIdentityProvidersResponse,
|
|
||||||
IdentityProvider,
|
|
||||||
ZitadelServer,
|
|
||||||
settings,
|
|
||||||
} from "@zitadel/server";
|
|
||||||
|
|
||||||
function getIdentityProviders(
|
function getIdentityProviders(orgId?: string) {
|
||||||
server: ZitadelServer,
|
|
||||||
orgId?: string,
|
|
||||||
): Promise<IdentityProvider[] | undefined> {
|
|
||||||
const settingsService = settings.getSettings(server);
|
|
||||||
return settingsService
|
return settingsService
|
||||||
.getActiveIdentityProviders(
|
.getActiveIdentityProviders({ ctx: makeReqCtx(orgId) }, {})
|
||||||
orgId ? { ctx: { orgId } } : { ctx: { instance: true } },
|
.then((resp) => {
|
||||||
{},
|
|
||||||
)
|
|
||||||
.then((resp: GetActiveIdentityProvidersResponse) => {
|
|
||||||
return resp.identityProviders;
|
return resp.identityProviders;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -39,16 +27,16 @@ export default async function Page({
|
|||||||
const organization = searchParams?.organization;
|
const organization = searchParams?.organization;
|
||||||
const submit: boolean = searchParams?.submit === "true";
|
const submit: boolean = searchParams?.submit === "true";
|
||||||
|
|
||||||
const loginSettings = await getLoginSettings(server, organization);
|
const loginSettings = await getLoginSettings(organization);
|
||||||
const legal = await getLegalAndSupportSettings(server);
|
const legal = await getLegalAndSupportSettings();
|
||||||
|
|
||||||
const identityProviders = await getIdentityProviders(server, organization);
|
const identityProviders = await getIdentityProviders(organization);
|
||||||
|
|
||||||
const host = process.env.VERCEL_URL
|
const host = process.env.VERCEL_URL
|
||||||
? `https://${process.env.VERCEL_URL}`
|
? `https://${process.env.VERCEL_URL}`
|
||||||
: "http://localhost:3000";
|
: "http://localhost:3000";
|
||||||
|
|
||||||
const branding = await getBrandingSettings(server, organization);
|
const branding = await getBrandingSettings(organization);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DynamicTheme branding={branding}>
|
<DynamicTheme branding={branding}>
|
||||||
|
|||||||
35
apps/login/src/app/(login)/mfa/create/page.tsx
Normal file
35
apps/login/src/app/(login)/mfa/create/page.tsx
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
"use client";
|
||||||
|
import { Button, ButtonVariants } from "@/ui/Button";
|
||||||
|
import { TextInput } from "@/ui/Input";
|
||||||
|
import UserAvatar from "@/ui/UserAvatar";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
|
||||||
|
export default function Page() {
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col items-center space-y-4">
|
||||||
|
<h1>Password</h1>
|
||||||
|
<p className="ztdl-p mb-6 block">Enter your password.</p>
|
||||||
|
|
||||||
|
<UserAvatar
|
||||||
|
showDropdown
|
||||||
|
displayName="Max Peintner"
|
||||||
|
loginName="max@zitadel.com"
|
||||||
|
></UserAvatar>
|
||||||
|
|
||||||
|
<div className="w-full">
|
||||||
|
<TextInput type="password" label="Password" />
|
||||||
|
</div>
|
||||||
|
<div className="flex w-full flex-row items-center justify-between">
|
||||||
|
<Button
|
||||||
|
onClick={() => router.back()}
|
||||||
|
variant={ButtonVariants.Secondary}
|
||||||
|
>
|
||||||
|
back
|
||||||
|
</Button>
|
||||||
|
<Button variant={ButtonVariants.Primary}>continue</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -2,7 +2,6 @@ import {
|
|||||||
getBrandingSettings,
|
getBrandingSettings,
|
||||||
getSession,
|
getSession,
|
||||||
listAuthenticationMethodTypes,
|
listAuthenticationMethodTypes,
|
||||||
server,
|
|
||||||
} from "@/lib/zitadel";
|
} from "@/lib/zitadel";
|
||||||
import Alert from "@/ui/Alert";
|
import Alert from "@/ui/Alert";
|
||||||
import ChooseSecondFactor from "@/ui/ChooseSecondFactor";
|
import ChooseSecondFactor from "@/ui/ChooseSecondFactor";
|
||||||
@@ -33,7 +32,7 @@ export default async function Page({
|
|||||||
loginName,
|
loginName,
|
||||||
organization,
|
organization,
|
||||||
);
|
);
|
||||||
return getSession(server, recent.id, recent.token).then((response) => {
|
return getSession(recent.id, recent.token).then((response) => {
|
||||||
if (response?.session && response.session.factors?.user?.id) {
|
if (response?.session && response.session.factors?.user?.id) {
|
||||||
return listAuthenticationMethodTypes(
|
return listAuthenticationMethodTypes(
|
||||||
response.session.factors.user.id,
|
response.session.factors.user.id,
|
||||||
@@ -49,7 +48,7 @@ export default async function Page({
|
|||||||
|
|
||||||
async function loadSessionById(sessionId: string, organization?: string) {
|
async function loadSessionById(sessionId: string, organization?: string) {
|
||||||
const recent = await getSessionCookieById(sessionId, organization);
|
const recent = await getSessionCookieById(sessionId, organization);
|
||||||
return getSession(server, recent.id, recent.token).then((response) => {
|
return getSession(recent.id, recent.token).then((response) => {
|
||||||
if (response?.session && response.session.factors?.user?.id) {
|
if (response?.session && response.session.factors?.user?.id) {
|
||||||
return listAuthenticationMethodTypes(
|
return listAuthenticationMethodTypes(
|
||||||
response.session.factors.user.id,
|
response.session.factors.user.id,
|
||||||
@@ -63,7 +62,7 @@ export default async function Page({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const branding = await getBrandingSettings(server, organization);
|
const branding = await getBrandingSettings(organization);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DynamicTheme branding={branding}>
|
<DynamicTheme branding={branding}>
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import {
|
|||||||
getSession,
|
getSession,
|
||||||
getUserByID,
|
getUserByID,
|
||||||
listAuthenticationMethodTypes,
|
listAuthenticationMethodTypes,
|
||||||
server,
|
|
||||||
} from "@/lib/zitadel";
|
} from "@/lib/zitadel";
|
||||||
import Alert from "@/ui/Alert";
|
import Alert from "@/ui/Alert";
|
||||||
import ChooseSecondFactorToSetup from "@/ui/ChooseSecondFactorToSetup";
|
import ChooseSecondFactorToSetup from "@/ui/ChooseSecondFactorToSetup";
|
||||||
@@ -14,7 +13,6 @@ import {
|
|||||||
getMostRecentCookieWithLoginname,
|
getMostRecentCookieWithLoginname,
|
||||||
getSessionCookieById,
|
getSessionCookieById,
|
||||||
} from "@/utils/cookies";
|
} from "@/utils/cookies";
|
||||||
import { user } from "@zitadel/server";
|
|
||||||
|
|
||||||
export default async function Page({
|
export default async function Page({
|
||||||
searchParams,
|
searchParams,
|
||||||
@@ -36,16 +34,21 @@ export default async function Page({
|
|||||||
loginName,
|
loginName,
|
||||||
organization,
|
organization,
|
||||||
);
|
);
|
||||||
return getSession(server, recent.id, recent.token).then((response) => {
|
return getSession(recent.id, recent.token).then((response) => {
|
||||||
if (response?.session && response.session.factors?.user?.id) {
|
if (response?.session && response.session.factors?.user?.id) {
|
||||||
const userId = response.session.factors.user.id;
|
const userId = response.session.factors.user.id;
|
||||||
return listAuthenticationMethodTypes(userId).then((methods) => {
|
return listAuthenticationMethodTypes(userId).then((methods) => {
|
||||||
return getUserByID(userId).then((user) => {
|
return getUserByID(userId).then((user) => {
|
||||||
|
const humanUser =
|
||||||
|
user.user?.type.case === "human"
|
||||||
|
? user.user?.type.value
|
||||||
|
: undefined;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
factors: response.session?.factors,
|
factors: response.session?.factors,
|
||||||
authMethods: methods.authMethodTypes ?? [],
|
authMethods: methods.authMethodTypes ?? [],
|
||||||
phoneVerified: user.user?.human?.phone?.isVerified ?? false,
|
phoneVerified: humanUser?.phone?.isVerified ?? false,
|
||||||
emailVerified: user.user?.human?.email?.isVerified ?? false,
|
emailVerified: humanUser?.email?.isVerified ?? false,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -55,16 +58,20 @@ export default async function Page({
|
|||||||
|
|
||||||
async function loadSessionById(sessionId: string, organization?: string) {
|
async function loadSessionById(sessionId: string, organization?: string) {
|
||||||
const recent = await getSessionCookieById(sessionId, organization);
|
const recent = await getSessionCookieById(sessionId, organization);
|
||||||
return getSession(server, recent.id, recent.token).then((response) => {
|
return getSession(recent.id, recent.token).then((response) => {
|
||||||
if (response?.session && response.session.factors?.user?.id) {
|
if (response?.session && response.session.factors?.user?.id) {
|
||||||
const userId = response.session.factors.user.id;
|
const userId = response.session.factors.user.id;
|
||||||
return listAuthenticationMethodTypes(userId).then((methods) => {
|
return listAuthenticationMethodTypes(userId).then((methods) => {
|
||||||
return getUserByID(userId).then((user) => {
|
return getUserByID(userId).then((user) => {
|
||||||
|
const humanUser =
|
||||||
|
user.user?.type.case === "human"
|
||||||
|
? user.user?.type.value
|
||||||
|
: undefined;
|
||||||
return {
|
return {
|
||||||
factors: response.session?.factors,
|
factors: response.session?.factors,
|
||||||
authMethods: methods.authMethodTypes ?? [],
|
authMethods: methods.authMethodTypes ?? [],
|
||||||
phoneVerified: user.user?.human?.phone?.isVerified ?? false,
|
phoneVerified: humanUser?.phone?.isVerified ?? false,
|
||||||
emailVerified: user.user?.human?.email?.isVerified ?? false,
|
emailVerified: humanUser?.email?.isVerified ?? false,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -72,8 +79,8 @@ export default async function Page({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const branding = await getBrandingSettings(server, organization);
|
const branding = await getBrandingSettings(organization);
|
||||||
const loginSettings = await getLoginSettings(server, organization);
|
const loginSettings = await getLoginSettings(organization);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DynamicTheme branding={branding}>
|
<DynamicTheme branding={branding}>
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import {
|
|||||||
getBrandingSettings,
|
getBrandingSettings,
|
||||||
getLoginSettings,
|
getLoginSettings,
|
||||||
getSession,
|
getSession,
|
||||||
server,
|
|
||||||
} from "@/lib/zitadel";
|
} from "@/lib/zitadel";
|
||||||
import Alert from "@/ui/Alert";
|
import Alert from "@/ui/Alert";
|
||||||
import DynamicTheme from "@/ui/DynamicTheme";
|
import DynamicTheme from "@/ui/DynamicTheme";
|
||||||
@@ -24,7 +23,7 @@ export default async function Page({
|
|||||||
|
|
||||||
const { session, token } = await loadSession(loginName, organization);
|
const { session, token } = await loadSession(loginName, organization);
|
||||||
|
|
||||||
const branding = await getBrandingSettings(server, organization);
|
const branding = await getBrandingSettings(organization);
|
||||||
|
|
||||||
async function loadSession(loginName?: string, organization?: string) {
|
async function loadSession(loginName?: string, organization?: string) {
|
||||||
const recent = await getMostRecentCookieWithLoginname(
|
const recent = await getMostRecentCookieWithLoginname(
|
||||||
@@ -32,7 +31,7 @@ export default async function Page({
|
|||||||
organization,
|
organization,
|
||||||
);
|
);
|
||||||
|
|
||||||
return getSession(server, recent.id, recent.token).then((response) => {
|
return getSession(recent.id, recent.token).then((response) => {
|
||||||
return { session: response?.session, token: recent.token };
|
return { session: response?.session, token: recent.token };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import {
|
|||||||
getBrandingSettings,
|
getBrandingSettings,
|
||||||
getSession,
|
getSession,
|
||||||
registerTOTP,
|
registerTOTP,
|
||||||
server,
|
|
||||||
} from "@/lib/zitadel";
|
} from "@/lib/zitadel";
|
||||||
import Alert from "@/ui/Alert";
|
import Alert from "@/ui/Alert";
|
||||||
import { Button, ButtonVariants } from "@/ui/Button";
|
import { Button, ButtonVariants } from "@/ui/Button";
|
||||||
@@ -13,9 +12,8 @@ import { Spinner } from "@/ui/Spinner";
|
|||||||
import TOTPRegister from "@/ui/TOTPRegister";
|
import TOTPRegister from "@/ui/TOTPRegister";
|
||||||
import UserAvatar from "@/ui/UserAvatar";
|
import UserAvatar from "@/ui/UserAvatar";
|
||||||
import { getMostRecentCookieWithLoginname } from "@/utils/cookies";
|
import { getMostRecentCookieWithLoginname } from "@/utils/cookies";
|
||||||
import { RegisterTOTPResponse } from "@zitadel/server";
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { ClientError } from "nice-grpc";
|
import { RegisterTOTPResponse } from "@zitadel/proto/zitadel/user/v2beta/user_service_pb";
|
||||||
|
|
||||||
export default async function Page({
|
export default async function Page({
|
||||||
searchParams,
|
searchParams,
|
||||||
@@ -28,11 +26,11 @@ export default async function Page({
|
|||||||
searchParams;
|
searchParams;
|
||||||
const { method } = params;
|
const { method } = params;
|
||||||
|
|
||||||
const branding = await getBrandingSettings(server, organization);
|
const branding = await getBrandingSettings(organization);
|
||||||
const { session, token } = await loadSession(loginName, organization);
|
const { session, token } = await loadSession(loginName, organization);
|
||||||
|
|
||||||
let totpResponse: RegisterTOTPResponse | undefined,
|
let totpResponse: RegisterTOTPResponse | undefined,
|
||||||
totpError: ClientError | undefined;
|
totpError: Error | undefined;
|
||||||
if (session && session.factors?.user?.id) {
|
if (session && session.factors?.user?.id) {
|
||||||
if (method === "time-based") {
|
if (method === "time-based") {
|
||||||
await registerTOTP(session.factors.user.id)
|
await registerTOTP(session.factors.user.id)
|
||||||
@@ -63,7 +61,7 @@ export default async function Page({
|
|||||||
organization,
|
organization,
|
||||||
);
|
);
|
||||||
|
|
||||||
return getSession(server, recent.id, recent.token).then((response) => {
|
return getSession(recent.id, recent.token).then((response) => {
|
||||||
return { session: response?.session, token: recent.token };
|
return { session: response?.session, token: recent.token };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -111,7 +109,7 @@ export default async function Page({
|
|||||||
|
|
||||||
{totpError && (
|
{totpError && (
|
||||||
<div className="py-4">
|
<div className="py-4">
|
||||||
<Alert>{totpError?.details}</Alert>
|
<Alert>{totpError?.message}</Alert>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { getBrandingSettings, getSession, server } from "@/lib/zitadel";
|
import { getBrandingSettings, getSession } from "@/lib/zitadel";
|
||||||
import Alert, { AlertType } from "@/ui/Alert";
|
import Alert, { AlertType } from "@/ui/Alert";
|
||||||
import DynamicTheme from "@/ui/DynamicTheme";
|
import DynamicTheme from "@/ui/DynamicTheme";
|
||||||
import RegisterPasskey from "@/ui/RegisterPasskey";
|
import RegisterPasskey from "@/ui/RegisterPasskey";
|
||||||
@@ -20,7 +20,7 @@ export default async function Page({
|
|||||||
loginName,
|
loginName,
|
||||||
organization,
|
organization,
|
||||||
);
|
);
|
||||||
return getSession(server, recent.id, recent.token).then((response) => {
|
return getSession(recent.id, recent.token).then((response) => {
|
||||||
if (response?.session) {
|
if (response?.session) {
|
||||||
return response.session;
|
return response.session;
|
||||||
}
|
}
|
||||||
@@ -33,7 +33,7 @@ export default async function Page({
|
|||||||
? "When set up, you will be able to authenticate without a password."
|
? "When set up, you will be able to authenticate without a password."
|
||||||
: "Your device will ask for your fingerprint, face, or screen lock";
|
: "Your device will ask for your fingerprint, face, or screen lock";
|
||||||
|
|
||||||
const branding = await getBrandingSettings(server, organization);
|
const branding = await getBrandingSettings(organization);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DynamicTheme branding={branding}>
|
<DynamicTheme branding={branding}>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { getBrandingSettings, getSession, server } from "@/lib/zitadel";
|
import { getBrandingSettings, getSession } from "@/lib/zitadel";
|
||||||
import Alert from "@/ui/Alert";
|
import Alert from "@/ui/Alert";
|
||||||
import DynamicTheme from "@/ui/DynamicTheme";
|
import DynamicTheme from "@/ui/DynamicTheme";
|
||||||
import LoginPasskey from "@/ui/LoginPasskey";
|
import LoginPasskey from "@/ui/LoginPasskey";
|
||||||
@@ -32,7 +32,7 @@ export default async function Page({
|
|||||||
loginName,
|
loginName,
|
||||||
organization,
|
organization,
|
||||||
);
|
);
|
||||||
return getSession(server, recent.id, recent.token).then((response) => {
|
return getSession(recent.id, recent.token).then((response) => {
|
||||||
if (response?.session) {
|
if (response?.session) {
|
||||||
return response.session;
|
return response.session;
|
||||||
}
|
}
|
||||||
@@ -41,14 +41,14 @@ export default async function Page({
|
|||||||
|
|
||||||
async function loadSessionById(sessionId: string, organization?: string) {
|
async function loadSessionById(sessionId: string, organization?: string) {
|
||||||
const recent = await getSessionCookieById(sessionId, organization);
|
const recent = await getSessionCookieById(sessionId, organization);
|
||||||
return getSession(server, recent.id, recent.token).then((response) => {
|
return getSession(recent.id, recent.token).then((response) => {
|
||||||
if (response?.session) {
|
if (response?.session) {
|
||||||
return response.session;
|
return response.session;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const branding = await getBrandingSettings(server, organization);
|
const branding = await getBrandingSettings(organization);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DynamicTheme branding={branding}>
|
<DynamicTheme branding={branding}>
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import {
|
|||||||
getBrandingSettings,
|
getBrandingSettings,
|
||||||
getLoginSettings,
|
getLoginSettings,
|
||||||
getSession,
|
getSession,
|
||||||
server,
|
|
||||||
} from "@/lib/zitadel";
|
} from "@/lib/zitadel";
|
||||||
import Alert from "@/ui/Alert";
|
import Alert from "@/ui/Alert";
|
||||||
import DynamicTheme from "@/ui/DynamicTheme";
|
import DynamicTheme from "@/ui/DynamicTheme";
|
||||||
@@ -25,15 +24,15 @@ export default async function Page({
|
|||||||
organization,
|
organization,
|
||||||
);
|
);
|
||||||
|
|
||||||
return getSession(server, recent.id, recent.token).then((response) => {
|
return getSession(recent.id, recent.token).then((response) => {
|
||||||
if (response?.session) {
|
if (response?.session) {
|
||||||
return response.session;
|
return response.session;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const branding = await getBrandingSettings(server, organization);
|
const branding = await getBrandingSettings(organization);
|
||||||
const loginSettings = await getLoginSettings(server, organization);
|
const loginSettings = await getLoginSettings(organization);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DynamicTheme branding={branding}>
|
<DynamicTheme branding={branding}>
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import {
|
|||||||
getBrandingSettings,
|
getBrandingSettings,
|
||||||
getLegalAndSupportSettings,
|
getLegalAndSupportSettings,
|
||||||
getPasswordComplexitySettings,
|
getPasswordComplexitySettings,
|
||||||
server,
|
|
||||||
} from "@/lib/zitadel";
|
} from "@/lib/zitadel";
|
||||||
import DynamicTheme from "@/ui/DynamicTheme";
|
import DynamicTheme from "@/ui/DynamicTheme";
|
||||||
import RegisterFormWithoutPassword from "@/ui/RegisterFormWithoutPassword";
|
import RegisterFormWithoutPassword from "@/ui/RegisterFormWithoutPassword";
|
||||||
@@ -18,13 +17,11 @@ export default async function Page({
|
|||||||
|
|
||||||
const setPassword = !!(firstname && lastname && email);
|
const setPassword = !!(firstname && lastname && email);
|
||||||
|
|
||||||
const legal = await getLegalAndSupportSettings(server, organization);
|
const legal = await getLegalAndSupportSettings(organization);
|
||||||
const passwordComplexitySettings = await getPasswordComplexitySettings(
|
const passwordComplexitySettings =
|
||||||
server,
|
await getPasswordComplexitySettings(organization);
|
||||||
organization,
|
|
||||||
);
|
|
||||||
|
|
||||||
const branding = await getBrandingSettings(server, organization);
|
const branding = await getBrandingSettings(organization);
|
||||||
|
|
||||||
return setPassword ? (
|
return setPassword ? (
|
||||||
<DynamicTheme branding={branding}>
|
<DynamicTheme branding={branding}>
|
||||||
|
|||||||
@@ -1,9 +1,4 @@
|
|||||||
import {
|
import { createCallback, getBrandingSettings, getSession } from "@/lib/zitadel";
|
||||||
createCallback,
|
|
||||||
getBrandingSettings,
|
|
||||||
getSession,
|
|
||||||
server,
|
|
||||||
} from "@/lib/zitadel";
|
|
||||||
import DynamicTheme from "@/ui/DynamicTheme";
|
import DynamicTheme from "@/ui/DynamicTheme";
|
||||||
import UserAvatar from "@/ui/UserAvatar";
|
import UserAvatar from "@/ui/UserAvatar";
|
||||||
import { getMostRecentCookieWithLoginname } from "@/utils/cookies";
|
import { getMostRecentCookieWithLoginname } from "@/utils/cookies";
|
||||||
@@ -13,14 +8,17 @@ async function loadSession(loginName: string, authRequestId?: string) {
|
|||||||
const recent = await getMostRecentCookieWithLoginname(`${loginName}`);
|
const recent = await getMostRecentCookieWithLoginname(`${loginName}`);
|
||||||
|
|
||||||
if (authRequestId) {
|
if (authRequestId) {
|
||||||
return createCallback(server, {
|
return createCallback({
|
||||||
authRequestId,
|
authRequestId,
|
||||||
session: { sessionId: recent.id, sessionToken: recent.token },
|
callbackKind: {
|
||||||
|
case: "session",
|
||||||
|
value: { sessionId: recent.id, sessionToken: recent.token },
|
||||||
|
},
|
||||||
}).then(({ callbackUrl }) => {
|
}).then(({ callbackUrl }) => {
|
||||||
return redirect(callbackUrl);
|
return redirect(callbackUrl);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return getSession(server, recent.id, recent.token).then((response) => {
|
return getSession(recent.id, recent.token).then((response) => {
|
||||||
if (response?.session) {
|
if (response?.session) {
|
||||||
return response.session;
|
return response.session;
|
||||||
}
|
}
|
||||||
@@ -31,7 +29,7 @@ export default async function Page({ searchParams }: { searchParams: any }) {
|
|||||||
const { loginName, authRequestId, organization } = searchParams;
|
const { loginName, authRequestId, organization } = searchParams;
|
||||||
const sessionFactors = await loadSession(loginName, authRequestId);
|
const sessionFactors = await loadSession(loginName, authRequestId);
|
||||||
|
|
||||||
const branding = await getBrandingSettings(server, organization);
|
const branding = await getBrandingSettings(organization);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DynamicTheme branding={branding}>
|
<DynamicTheme branding={branding}>
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import {
|
|||||||
getBrandingSettings,
|
getBrandingSettings,
|
||||||
getLoginSettings,
|
getLoginSettings,
|
||||||
getSession,
|
getSession,
|
||||||
server,
|
|
||||||
} from "@/lib/zitadel";
|
} from "@/lib/zitadel";
|
||||||
import Alert from "@/ui/Alert";
|
import Alert from "@/ui/Alert";
|
||||||
import DynamicTheme from "@/ui/DynamicTheme";
|
import DynamicTheme from "@/ui/DynamicTheme";
|
||||||
@@ -22,7 +21,7 @@ export default async function Page({
|
|||||||
}) {
|
}) {
|
||||||
const { loginName, authRequestId, sessionId, organization } = searchParams;
|
const { loginName, authRequestId, sessionId, organization } = searchParams;
|
||||||
|
|
||||||
const branding = await getBrandingSettings(server, organization);
|
const branding = await getBrandingSettings(organization);
|
||||||
|
|
||||||
const sessionFactors = sessionId
|
const sessionFactors = sessionId
|
||||||
? await loadSessionById(sessionId, organization)
|
? await loadSessionById(sessionId, organization)
|
||||||
@@ -36,7 +35,7 @@ export default async function Page({
|
|||||||
loginName,
|
loginName,
|
||||||
organization,
|
organization,
|
||||||
);
|
);
|
||||||
return getSession(server, recent.id, recent.token).then((response) => {
|
return getSession(recent.id, recent.token).then((response) => {
|
||||||
if (response?.session) {
|
if (response?.session) {
|
||||||
return response.session;
|
return response.session;
|
||||||
}
|
}
|
||||||
@@ -45,7 +44,7 @@ export default async function Page({
|
|||||||
|
|
||||||
async function loadSessionById(sessionId: string, organization?: string) {
|
async function loadSessionById(sessionId: string, organization?: string) {
|
||||||
const recent = await getSessionCookieById(sessionId, organization);
|
const recent = await getSessionCookieById(sessionId, organization);
|
||||||
return getSession(server, recent.id, recent.token).then((response) => {
|
return getSession(recent.id, recent.token).then((response) => {
|
||||||
if (response?.session) {
|
if (response?.session) {
|
||||||
return response.session;
|
return response.session;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { getBrandingSettings, getSession, server } from "@/lib/zitadel";
|
import { getBrandingSettings, getSession } from "@/lib/zitadel";
|
||||||
import Alert, { AlertType } from "@/ui/Alert";
|
import Alert, { AlertType } from "@/ui/Alert";
|
||||||
import DynamicTheme from "@/ui/DynamicTheme";
|
import DynamicTheme from "@/ui/DynamicTheme";
|
||||||
import RegisterPasskey from "@/ui/RegisterPasskey";
|
import RegisterPasskey from "@/ui/RegisterPasskey";
|
||||||
@@ -20,7 +20,7 @@ export default async function Page({
|
|||||||
loginName,
|
loginName,
|
||||||
organization,
|
organization,
|
||||||
);
|
);
|
||||||
return getSession(server, recent.id, recent.token).then((response) => {
|
return getSession(recent.id, recent.token).then((response) => {
|
||||||
if (response?.session) {
|
if (response?.session) {
|
||||||
return response.session;
|
return response.session;
|
||||||
}
|
}
|
||||||
@@ -30,7 +30,7 @@ export default async function Page({
|
|||||||
const description =
|
const description =
|
||||||
"Your device will ask for your fingerprint, face, or screen lock";
|
"Your device will ask for your fingerprint, face, or screen lock";
|
||||||
|
|
||||||
const branding = await getBrandingSettings(server, organization);
|
const branding = await getBrandingSettings(organization);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DynamicTheme branding={branding}>
|
<DynamicTheme branding={branding}>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { getBrandingSettings, server } from "@/lib/zitadel";
|
import { getBrandingSettings } from "@/lib/zitadel";
|
||||||
import DynamicTheme from "@/ui/DynamicTheme";
|
import DynamicTheme from "@/ui/DynamicTheme";
|
||||||
import VerifyEmailForm from "@/ui/VerifyEmailForm";
|
import VerifyEmailForm from "@/ui/VerifyEmailForm";
|
||||||
import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";
|
import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";
|
||||||
@@ -15,7 +15,7 @@ export default async function Page({ searchParams }: { searchParams: any }) {
|
|||||||
passwordset,
|
passwordset,
|
||||||
} = searchParams;
|
} = searchParams;
|
||||||
|
|
||||||
const branding = await getBrandingSettings(server, organization);
|
const branding = await getBrandingSettings(organization);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DynamicTheme branding={branding}>
|
<DynamicTheme branding={branding}>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { server, startIdentityProviderFlow } from "@/lib/zitadel";
|
import { startIdentityProviderFlow } from "@/lib/zitadel";
|
||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
|
||||||
export async function POST(request: NextRequest) {
|
export async function POST(request: NextRequest) {
|
||||||
@@ -6,7 +6,7 @@ export async function POST(request: NextRequest) {
|
|||||||
if (body) {
|
if (body) {
|
||||||
let { idpId, successUrl, failureUrl } = body;
|
let { idpId, successUrl, failureUrl } = body;
|
||||||
|
|
||||||
return startIdentityProviderFlow(server, {
|
return startIdentityProviderFlow({
|
||||||
idpId,
|
idpId,
|
||||||
urls: {
|
urls: {
|
||||||
successUrl,
|
successUrl,
|
||||||
|
|||||||
@@ -7,11 +7,7 @@ export async function POST(request: NextRequest) {
|
|||||||
if (body) {
|
if (body) {
|
||||||
const { loginName, authRequestId, organization } = body;
|
const { loginName, authRequestId, organization } = body;
|
||||||
return listUsers(loginName, organization).then((users) => {
|
return listUsers(loginName, organization).then((users) => {
|
||||||
if (
|
if (users.details?.totalResult == BigInt(1) && users.result[0].userId) {
|
||||||
users.details &&
|
|
||||||
users.details.totalResult == 1 &&
|
|
||||||
users.result[0].userId
|
|
||||||
) {
|
|
||||||
const userId = users.result[0].userId;
|
const userId = users.result[0].userId;
|
||||||
return createSessionForUserIdAndUpdateCookie(
|
return createSessionForUserIdAndUpdateCookie(
|
||||||
userId,
|
userId,
|
||||||
|
|||||||
@@ -5,8 +5,9 @@ import {
|
|||||||
getSessionCookieByLoginName,
|
getSessionCookieByLoginName,
|
||||||
} from "@/utils/cookies";
|
} from "@/utils/cookies";
|
||||||
import { setSessionAndUpdateCookie } from "@/utils/session";
|
import { setSessionAndUpdateCookie } from "@/utils/session";
|
||||||
import { Checks } from "@zitadel/server";
|
|
||||||
import { NextRequest, NextResponse, userAgent } from "next/server";
|
import { NextRequest, NextResponse, userAgent } from "next/server";
|
||||||
|
import { Checks } from "@zitadel/proto/zitadel/session/v2beta/session_service_pb";
|
||||||
|
import { PlainMessage } from "@zitadel/client2";
|
||||||
|
|
||||||
export async function POST(request: NextRequest) {
|
export async function POST(request: NextRequest) {
|
||||||
const body = await request.json();
|
const body = await request.json();
|
||||||
@@ -31,7 +32,7 @@ export async function POST(request: NextRequest) {
|
|||||||
|
|
||||||
return recentPromise
|
return recentPromise
|
||||||
.then((recent) => {
|
.then((recent) => {
|
||||||
const checks: Checks = {};
|
const checks: PlainMessage<Checks> = {};
|
||||||
|
|
||||||
if (method === "time-based") {
|
if (method === "time-based") {
|
||||||
checks.totp = {
|
checks.totp = {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import {
|
|||||||
createPasskeyRegistrationLink,
|
createPasskeyRegistrationLink,
|
||||||
getSession,
|
getSession,
|
||||||
registerPasskey,
|
registerPasskey,
|
||||||
server,
|
|
||||||
} from "@/lib/zitadel";
|
} from "@/lib/zitadel";
|
||||||
import { getSessionCookieById } from "@/utils/cookies";
|
import { getSessionCookieById } from "@/utils/cookies";
|
||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
@@ -14,11 +13,7 @@ export async function POST(request: NextRequest) {
|
|||||||
|
|
||||||
const sessionCookie = await getSessionCookieById(sessionId);
|
const sessionCookie = await getSessionCookieById(sessionId);
|
||||||
|
|
||||||
const session = await getSession(
|
const session = await getSession(sessionCookie.id, sessionCookie.token);
|
||||||
server,
|
|
||||||
sessionCookie.id,
|
|
||||||
sessionCookie.token,
|
|
||||||
);
|
|
||||||
|
|
||||||
const domain: string = request.nextUrl.hostname;
|
const domain: string = request.nextUrl.hostname;
|
||||||
|
|
||||||
@@ -29,6 +24,9 @@ export async function POST(request: NextRequest) {
|
|||||||
return createPasskeyRegistrationLink(userId)
|
return createPasskeyRegistrationLink(userId)
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
const code = resp.code;
|
const code = resp.code;
|
||||||
|
if (!code) {
|
||||||
|
throw new Error("Missing code in response");
|
||||||
|
}
|
||||||
return registerPasskey(userId, code, domain).then((resp) => {
|
return registerPasskey(userId, code, domain).then((resp) => {
|
||||||
return NextResponse.json(resp);
|
return NextResponse.json(resp);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { getSession, server, verifyPasskeyRegistration } from "@/lib/zitadel";
|
import { getSession, verifyPasskeyRegistration } from "@/lib/zitadel";
|
||||||
import { getSessionCookieById } from "@/utils/cookies";
|
import { getSessionCookieById } from "@/utils/cookies";
|
||||||
import { NextRequest, NextResponse, userAgent } from "next/server";
|
import { NextRequest, NextResponse, userAgent } from "next/server";
|
||||||
|
|
||||||
@@ -15,17 +15,12 @@ export async function POST(request: NextRequest) {
|
|||||||
}
|
}
|
||||||
const sessionCookie = await getSessionCookieById(sessionId);
|
const sessionCookie = await getSessionCookieById(sessionId);
|
||||||
|
|
||||||
const session = await getSession(
|
const session = await getSession(sessionCookie.id, sessionCookie.token);
|
||||||
server,
|
|
||||||
sessionCookie.id,
|
|
||||||
sessionCookie.token,
|
|
||||||
);
|
|
||||||
|
|
||||||
const userId = session?.session?.factors?.user?.id;
|
const userId = session?.session?.factors?.user?.id;
|
||||||
|
|
||||||
if (userId) {
|
if (userId) {
|
||||||
return verifyPasskeyRegistration(
|
return verifyPasskeyRegistration(
|
||||||
server,
|
|
||||||
passkeyId,
|
passkeyId,
|
||||||
passkeyName,
|
passkeyName,
|
||||||
publicKeyCredential,
|
publicKeyCredential,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { addHumanUser, server } from "@/lib/zitadel";
|
import { addHumanUser } from "@/lib/zitadel";
|
||||||
import {
|
import {
|
||||||
createSessionAndUpdateCookie,
|
createSessionAndUpdateCookie,
|
||||||
createSessionForUserIdAndUpdateCookie,
|
createSessionForUserIdAndUpdateCookie,
|
||||||
@@ -17,7 +17,7 @@ export async function POST(request: NextRequest) {
|
|||||||
authRequestId,
|
authRequestId,
|
||||||
} = body;
|
} = body;
|
||||||
|
|
||||||
return addHumanUser(server, {
|
return addHumanUser({
|
||||||
email: email,
|
email: email,
|
||||||
firstName,
|
firstName,
|
||||||
lastName,
|
lastName,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { setEmail, server } from "@/lib/zitadel";
|
import { setEmail } from "@/lib/zitadel";
|
||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
|
||||||
export async function POST(request: NextRequest) {
|
export async function POST(request: NextRequest) {
|
||||||
@@ -7,7 +7,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const { userId, code } = body;
|
const { userId, code } = body;
|
||||||
|
|
||||||
// replace with resend Mail method once its implemented
|
// replace with resend Mail method once its implemented
|
||||||
return setEmail(server, userId)
|
return setEmail(userId)
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
return NextResponse.json(resp);
|
return NextResponse.json(resp);
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import {
|
import {
|
||||||
server,
|
|
||||||
deleteSession,
|
deleteSession,
|
||||||
getSession,
|
getSession,
|
||||||
getUserByID,
|
getUserByID,
|
||||||
@@ -17,7 +16,6 @@ import {
|
|||||||
createSessionForIdpAndUpdateCookie,
|
createSessionForIdpAndUpdateCookie,
|
||||||
setSessionAndUpdateCookie,
|
setSessionAndUpdateCookie,
|
||||||
} from "@/utils/session";
|
} from "@/utils/session";
|
||||||
import { Challenges, Checks, RequestChallenges } from "@zitadel/server";
|
|
||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
|
||||||
export async function POST(request: NextRequest) {
|
export async function POST(request: NextRequest) {
|
||||||
@@ -104,27 +102,23 @@ export async function PUT(request: NextRequest) {
|
|||||||
challenges &&
|
challenges &&
|
||||||
(challenges.otpEmail === "" || challenges.otpSms === "")
|
(challenges.otpEmail === "" || challenges.otpSms === "")
|
||||||
) {
|
) {
|
||||||
const sessionResponse = await getSession(
|
const sessionResponse = await getSession(recent.id, recent.token);
|
||||||
server,
|
|
||||||
recent.id,
|
|
||||||
recent.token,
|
|
||||||
);
|
|
||||||
if (sessionResponse && sessionResponse.session?.factors?.user?.id) {
|
if (sessionResponse && sessionResponse.session?.factors?.user?.id) {
|
||||||
const userResponse = await getUserByID(
|
const userResponse = await getUserByID(
|
||||||
sessionResponse.session.factors.user.id,
|
sessionResponse.session.factors.user.id,
|
||||||
);
|
);
|
||||||
if (
|
const humanUser =
|
||||||
challenges.otpEmail === "" &&
|
userResponse.user?.type.case === "human"
|
||||||
userResponse.user?.human?.email?.email
|
? userResponse.user?.type.value
|
||||||
) {
|
: undefined;
|
||||||
challenges.otpEmail = userResponse.user?.human?.email?.email;
|
|
||||||
|
if (challenges.otpEmail === "" && humanUser?.email?.email) {
|
||||||
|
challenges.otpEmail = humanUser?.email?.email;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (challenges.otpSms === "" && humanUser?.phone?.phone) {
|
||||||
challenges.otpSms === "" &&
|
challenges.otpSms = humanUser?.phone?.phone;
|
||||||
userResponse.user?.human?.phone?.phone
|
|
||||||
) {
|
|
||||||
challenges.otpSms = userResponse.user?.human?.phone?.phone;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -176,7 +170,7 @@ export async function DELETE(request: NextRequest) {
|
|||||||
if (id) {
|
if (id) {
|
||||||
const session = await getSessionCookieById(id);
|
const session = await getSessionCookieById(id);
|
||||||
|
|
||||||
return deleteSession(server, session.id, session.token)
|
return deleteSession(session.id, session.token)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return removeSessionFromCookie(session)
|
return removeSessionFromCookie(session)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import {
|
|||||||
getSession,
|
getSession,
|
||||||
registerPasskey,
|
registerPasskey,
|
||||||
registerU2F,
|
registerU2F,
|
||||||
server,
|
|
||||||
} from "@/lib/zitadel";
|
} from "@/lib/zitadel";
|
||||||
import { getSessionCookieById } from "@/utils/cookies";
|
import { getSessionCookieById } from "@/utils/cookies";
|
||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
@@ -15,11 +14,7 @@ export async function POST(request: NextRequest) {
|
|||||||
|
|
||||||
const sessionCookie = await getSessionCookieById(sessionId);
|
const sessionCookie = await getSessionCookieById(sessionId);
|
||||||
|
|
||||||
const session = await getSession(
|
const session = await getSession(sessionCookie.id, sessionCookie.token);
|
||||||
server,
|
|
||||||
sessionCookie.id,
|
|
||||||
sessionCookie.token,
|
|
||||||
);
|
|
||||||
|
|
||||||
const domain: string = request.nextUrl.hostname;
|
const domain: string = request.nextUrl.hostname;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { getSession, server, verifyU2FRegistration } from "@/lib/zitadel";
|
import { getSession, verifyU2FRegistration } from "@/lib/zitadel";
|
||||||
import { getSessionCookieById } from "@/utils/cookies";
|
import { getSessionCookieById } from "@/utils/cookies";
|
||||||
import { VerifyU2FRegistrationRequest } from "@zitadel/server";
|
|
||||||
import { NextRequest, NextResponse, userAgent } from "next/server";
|
import { NextRequest, NextResponse, userAgent } from "next/server";
|
||||||
|
import { VerifyU2FRegistrationRequest } from "@zitadel/proto/zitadel/user/v2beta/user_service_pb";
|
||||||
|
import { PlainMessage } from "@zitadel/client2";
|
||||||
|
|
||||||
export async function POST(request: NextRequest) {
|
export async function POST(request: NextRequest) {
|
||||||
const body = await request.json();
|
const body = await request.json();
|
||||||
@@ -16,16 +17,12 @@ export async function POST(request: NextRequest) {
|
|||||||
}
|
}
|
||||||
const sessionCookie = await getSessionCookieById(sessionId);
|
const sessionCookie = await getSessionCookieById(sessionId);
|
||||||
|
|
||||||
const session = await getSession(
|
const session = await getSession(sessionCookie.id, sessionCookie.token);
|
||||||
server,
|
|
||||||
sessionCookie.id,
|
|
||||||
sessionCookie.token,
|
|
||||||
);
|
|
||||||
|
|
||||||
const userId = session?.session?.factors?.user?.id;
|
const userId = session?.session?.factors?.user?.id;
|
||||||
|
|
||||||
if (userId) {
|
if (userId) {
|
||||||
const req: VerifyU2FRegistrationRequest = {
|
const req: PlainMessage<VerifyU2FRegistrationRequest> = {
|
||||||
publicKeyCredential,
|
publicKeyCredential,
|
||||||
u2fId,
|
u2fId,
|
||||||
userId,
|
userId,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { server, verifyEmail } from "@/lib/zitadel";
|
import { verifyEmail } from "@/lib/zitadel";
|
||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
|
||||||
export async function POST(request: NextRequest) {
|
export async function POST(request: NextRequest) {
|
||||||
@@ -6,7 +6,7 @@ export async function POST(request: NextRequest) {
|
|||||||
if (body) {
|
if (body) {
|
||||||
const { userId, code } = body;
|
const { userId, code } = body;
|
||||||
|
|
||||||
return verifyEmail(server, userId, code)
|
return verifyEmail(userId, code)
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
return NextResponse.json(resp);
|
return NextResponse.json(resp);
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ import { LayoutProviders } from "@/ui/LayoutProviders";
|
|||||||
import { Analytics } from "@vercel/analytics/react";
|
import { Analytics } from "@vercel/analytics/react";
|
||||||
import ThemeWrapper from "@/ui/ThemeWrapper";
|
import ThemeWrapper from "@/ui/ThemeWrapper";
|
||||||
import { getBrandingSettings } from "@/lib/zitadel";
|
import { getBrandingSettings } from "@/lib/zitadel";
|
||||||
import { server } from "../lib/zitadel";
|
|
||||||
import { BrandingSettings } from "@zitadel/server";
|
|
||||||
import ThemeProvider from "@/ui/ThemeProvider";
|
import ThemeProvider from "@/ui/ThemeProvider";
|
||||||
import Theme from "@/ui/Theme";
|
import Theme from "@/ui/Theme";
|
||||||
|
|
||||||
|
|||||||
@@ -3,15 +3,17 @@ import {
|
|||||||
getAuthRequest,
|
getAuthRequest,
|
||||||
getOrgByDomain,
|
getOrgByDomain,
|
||||||
listSessions,
|
listSessions,
|
||||||
server,
|
|
||||||
} from "@/lib/zitadel";
|
} from "@/lib/zitadel";
|
||||||
import { SessionCookie, getAllSessions } from "@/utils/cookies";
|
import { SessionCookie, getAllSessions } from "@/utils/cookies";
|
||||||
import { Session, AuthRequest, Prompt } from "@zitadel/server";
|
|
||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
import { Session } from "@zitadel/proto/zitadel/session/v2beta/session_pb";
|
||||||
|
import {
|
||||||
|
AuthRequest,
|
||||||
|
Prompt,
|
||||||
|
} from "@zitadel/proto/zitadel/oidc/v2beta/authorization_pb";
|
||||||
|
|
||||||
async function loadSessions(ids: string[]): Promise<Session[]> {
|
async function loadSessions(ids: string[]): Promise<Session[]> {
|
||||||
const response = await listSessions(
|
const response = await listSessions(
|
||||||
server,
|
|
||||||
ids.filter((id: string | undefined) => !!id),
|
ids.filter((id: string | undefined) => !!id),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -81,9 +83,12 @@ export async function GET(request: NextRequest) {
|
|||||||
sessionToken: cookie?.token,
|
sessionToken: cookie?.token,
|
||||||
};
|
};
|
||||||
|
|
||||||
const { callbackUrl } = await createCallback(server, {
|
const { callbackUrl } = await createCallback({
|
||||||
authRequestId,
|
authRequestId,
|
||||||
session,
|
callbackKind: {
|
||||||
|
case: "session",
|
||||||
|
value: session,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
return NextResponse.redirect(callbackUrl);
|
return NextResponse.redirect(callbackUrl);
|
||||||
}
|
}
|
||||||
@@ -92,7 +97,7 @@ export async function GET(request: NextRequest) {
|
|||||||
|
|
||||||
if (authRequestId) {
|
if (authRequestId) {
|
||||||
console.log(`Login with authRequest: ${authRequestId}`);
|
console.log(`Login with authRequest: ${authRequestId}`);
|
||||||
const { authRequest } = await getAuthRequest(server, { authRequestId });
|
const { authRequest } = await getAuthRequest({ authRequestId });
|
||||||
|
|
||||||
let organization = "";
|
let organization = "";
|
||||||
|
|
||||||
@@ -132,7 +137,7 @@ export async function GET(request: NextRequest) {
|
|||||||
return NextResponse.redirect(accountsUrl);
|
return NextResponse.redirect(accountsUrl);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (authRequest && authRequest.prompt.includes(Prompt.PROMPT_CREATE)) {
|
if (authRequest && authRequest.prompt.includes(Prompt.CREATE)) {
|
||||||
const registerUrl = new URL("/register", request.url);
|
const registerUrl = new URL("/register", request.url);
|
||||||
if (authRequest?.id) {
|
if (authRequest?.id) {
|
||||||
registerUrl.searchParams.set("authRequestId", authRequest?.id);
|
registerUrl.searchParams.set("authRequestId", authRequest?.id);
|
||||||
@@ -147,9 +152,9 @@ export async function GET(request: NextRequest) {
|
|||||||
// use existing session and hydrate it for oidc
|
// use existing session and hydrate it for oidc
|
||||||
if (authRequest && sessions.length) {
|
if (authRequest && sessions.length) {
|
||||||
// if some accounts are available for selection and select_account is set
|
// if some accounts are available for selection and select_account is set
|
||||||
if (authRequest.prompt.includes(Prompt.PROMPT_SELECT_ACCOUNT)) {
|
if (authRequest.prompt.includes(Prompt.SELECT_ACCOUNT)) {
|
||||||
return gotoAccounts();
|
return gotoAccounts();
|
||||||
} else if (authRequest.prompt.includes(Prompt.PROMPT_LOGIN)) {
|
} else if (authRequest.prompt.includes(Prompt.LOGIN)) {
|
||||||
// if prompt is login
|
// if prompt is login
|
||||||
const loginNameUrl = new URL("/loginname", request.url);
|
const loginNameUrl = new URL("/loginname", request.url);
|
||||||
if (authRequest?.id) {
|
if (authRequest?.id) {
|
||||||
@@ -162,7 +167,7 @@ export async function GET(request: NextRequest) {
|
|||||||
loginNameUrl.searchParams.set("organization", organization);
|
loginNameUrl.searchParams.set("organization", organization);
|
||||||
}
|
}
|
||||||
return NextResponse.redirect(loginNameUrl);
|
return NextResponse.redirect(loginNameUrl);
|
||||||
} else if (authRequest.prompt.includes(Prompt.PROMPT_NONE)) {
|
} else if (authRequest.prompt.includes(Prompt.NONE)) {
|
||||||
// NONE prompt - silent authentication
|
// NONE prompt - silent authentication
|
||||||
|
|
||||||
let selectedSession = findSession(sessions, authRequest);
|
let selectedSession = findSession(sessions, authRequest);
|
||||||
@@ -177,9 +182,12 @@ export async function GET(request: NextRequest) {
|
|||||||
sessionId: cookie?.id,
|
sessionId: cookie?.id,
|
||||||
sessionToken: cookie?.token,
|
sessionToken: cookie?.token,
|
||||||
};
|
};
|
||||||
const { callbackUrl } = await createCallback(server, {
|
const { callbackUrl } = await createCallback({
|
||||||
authRequestId,
|
authRequestId,
|
||||||
session,
|
callbackKind: {
|
||||||
|
case: "session",
|
||||||
|
value: session,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
return NextResponse.redirect(callbackUrl);
|
return NextResponse.redirect(callbackUrl);
|
||||||
} else {
|
} else {
|
||||||
@@ -209,9 +217,12 @@ export async function GET(request: NextRequest) {
|
|||||||
sessionToken: cookie?.token,
|
sessionToken: cookie?.token,
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
const { callbackUrl } = await createCallback(server, {
|
const { callbackUrl } = await createCallback({
|
||||||
authRequestId,
|
authRequestId,
|
||||||
session,
|
callbackKind: {
|
||||||
|
case: "session",
|
||||||
|
value: session,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
if (callbackUrl) {
|
if (callbackUrl) {
|
||||||
return NextResponse.redirect(callbackUrl);
|
return NextResponse.redirect(callbackUrl);
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export const demos: { name: string; items: Item[] }[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "IDP Register",
|
name: "IDP Register",
|
||||||
slug: "register/idp",
|
slug: "idp",
|
||||||
description: "Add a user from an external identity provider",
|
description: "Add a user from an external identity provider",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use server";
|
"use server";
|
||||||
|
|
||||||
import { getMostRecentCookieWithLoginname } from "@/utils/cookies";
|
import { getMostRecentCookieWithLoginname } from "@/utils/cookies";
|
||||||
import { getSession, server, verifyTOTPRegistration } from "./zitadel";
|
import { getSession, verifyTOTPRegistration } from "./zitadel";
|
||||||
|
|
||||||
export async function verifyTOTP(
|
export async function verifyTOTP(
|
||||||
code: string,
|
code: string,
|
||||||
@@ -10,7 +10,7 @@ export async function verifyTOTP(
|
|||||||
) {
|
) {
|
||||||
return getMostRecentCookieWithLoginname(loginName, organization)
|
return getMostRecentCookieWithLoginname(loginName, organization)
|
||||||
.then((recent) => {
|
.then((recent) => {
|
||||||
return getSession(server, recent.id, recent.token).then((response) => {
|
return getSession(recent.id, recent.token).then((response) => {
|
||||||
return { session: response?.session, token: recent.token };
|
return { session: response?.session, token: recent.token };
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,109 +1,51 @@
|
|||||||
import { VerifyU2FRegistrationRequest } from "@zitadel/server";
|
|
||||||
import {
|
import {
|
||||||
GetUserByIDResponse,
|
createOIDCServiceClient,
|
||||||
RegisterTOTPResponse,
|
createSessionServiceClient,
|
||||||
VerifyTOTPRegistrationResponse,
|
createSettingsServiceClient,
|
||||||
} from "@zitadel/server";
|
createUserServiceClient,
|
||||||
|
makeReqCtx,
|
||||||
|
} from "@zitadel/client2/v2beta";
|
||||||
|
import { createManagementServiceClient } from "@zitadel/client2/v1";
|
||||||
|
import { createServerTransport } from "@zitadel/node";
|
||||||
|
import { Checks } from "@zitadel/proto/zitadel/session/v2beta/session_service_pb";
|
||||||
|
import { RequestChallenges } from "@zitadel/proto/zitadel/session/v2beta/challenge_pb";
|
||||||
import {
|
import {
|
||||||
LegalAndSupportSettings,
|
|
||||||
PasswordComplexitySettings,
|
|
||||||
ZitadelServer,
|
|
||||||
VerifyMyAuthFactorOTPResponse,
|
|
||||||
ZitadelServerOptions,
|
|
||||||
user,
|
|
||||||
oidc,
|
|
||||||
settings,
|
|
||||||
getServers,
|
|
||||||
auth,
|
|
||||||
initializeServer,
|
|
||||||
session,
|
|
||||||
GetGeneralSettingsResponse,
|
|
||||||
CreateSessionResponse,
|
|
||||||
GetBrandingSettingsResponse,
|
|
||||||
GetPasswordComplexitySettingsResponse,
|
|
||||||
RegisterU2FResponse,
|
|
||||||
GetLegalAndSupportSettingsResponse,
|
|
||||||
AddHumanUserResponse,
|
|
||||||
BrandingSettings,
|
|
||||||
ListSessionsResponse,
|
|
||||||
GetSessionResponse,
|
|
||||||
VerifyEmailResponse,
|
|
||||||
Checks,
|
|
||||||
SetSessionResponse,
|
|
||||||
SetSessionRequest,
|
|
||||||
ListUsersResponse,
|
|
||||||
management,
|
|
||||||
DeleteSessionResponse,
|
|
||||||
VerifyPasskeyRegistrationResponse,
|
|
||||||
LoginSettings,
|
|
||||||
GetOrgByDomainGlobalResponse,
|
|
||||||
GetLoginSettingsResponse,
|
|
||||||
ListAuthenticationMethodTypesResponse,
|
|
||||||
StartIdentityProviderIntentRequest,
|
|
||||||
StartIdentityProviderIntentResponse,
|
|
||||||
RetrieveIdentityProviderIntentRequest,
|
RetrieveIdentityProviderIntentRequest,
|
||||||
RetrieveIdentityProviderIntentResponse,
|
VerifyU2FRegistrationRequest,
|
||||||
GetAuthRequestResponse,
|
} from "@zitadel/proto/zitadel/user/v2beta/user_service_pb";
|
||||||
GetAuthRequestRequest,
|
import {
|
||||||
CreateCallbackRequest,
|
CreateCallbackRequest,
|
||||||
CreateCallbackResponse,
|
} from "@zitadel/proto/zitadel/oidc/v2beta/oidc_service_pb";
|
||||||
RequestChallenges,
|
import { TextQueryMethod } from "@zitadel/proto/zitadel/object/v2beta/object_pb";
|
||||||
TextQueryMethod,
|
import type { RedirectURLs } from "@zitadel/proto/zitadel/user/v2beta/idp_pb";
|
||||||
ListHumanAuthFactorsResponse,
|
import { PlainMessage } from "@zitadel/client2";
|
||||||
AddHumanUserRequest,
|
|
||||||
AddOTPEmailResponse,
|
|
||||||
AddOTPSMSResponse,
|
|
||||||
} from "@zitadel/server";
|
|
||||||
|
|
||||||
const SESSION_LIFETIME_S = 3000;
|
const SESSION_LIFETIME_S = 3000;
|
||||||
|
|
||||||
export const zitadelConfig: ZitadelServerOptions = {
|
const transport = createServerTransport(
|
||||||
name: "zitadel login",
|
process.env.ZITADEL_SERVICE_USER_TOKEN!,
|
||||||
apiUrl: process.env.ZITADEL_API_URL ?? "",
|
{baseUrl: process.env.ZITADEL_API_URL!},
|
||||||
token: process.env.ZITADEL_SERVICE_USER_TOKEN ?? "",
|
);
|
||||||
};
|
|
||||||
|
|
||||||
let server: ZitadelServer;
|
export const sessionService = createSessionServiceClient(transport);
|
||||||
|
export const managementService = createManagementServiceClient(transport);
|
||||||
|
export const userService = createUserServiceClient(transport);
|
||||||
|
export const oidcService = createOIDCServiceClient(transport);
|
||||||
|
export const settingsService = createSettingsServiceClient(transport);
|
||||||
|
|
||||||
if (!getServers().length) {
|
export async function getBrandingSettings(organization?: string) {
|
||||||
console.log("initialize server");
|
|
||||||
server = initializeServer(zitadelConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getBrandingSettings(
|
|
||||||
server: ZitadelServer,
|
|
||||||
organization?: string,
|
|
||||||
): Promise<BrandingSettings | undefined> {
|
|
||||||
const settingsService = settings.getSettings(server);
|
|
||||||
return settingsService
|
return settingsService
|
||||||
.getBrandingSettings(
|
.getBrandingSettings({ ctx: makeReqCtx(organization) }, {})
|
||||||
{ ctx: organization ? { orgId: organization } : { instance: true } },
|
.then((resp) => resp.settings);
|
||||||
{},
|
|
||||||
)
|
|
||||||
.then((resp: GetBrandingSettingsResponse) => resp.settings);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getLoginSettings(
|
export async function getLoginSettings(orgId?: string) {
|
||||||
server: ZitadelServer,
|
|
||||||
orgId?: string,
|
|
||||||
): Promise<LoginSettings | undefined> {
|
|
||||||
const settingsService = settings.getSettings(server);
|
|
||||||
return settingsService
|
return settingsService
|
||||||
.getLoginSettings({ ctx: orgId ? { orgId } : { instance: true } }, {})
|
.getLoginSettings({ ctx: makeReqCtx(orgId) }, {})
|
||||||
.then((resp: GetLoginSettingsResponse) => resp.settings);
|
.then((resp) => resp.settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function verifyMyAuthFactorOTP(
|
export async function addOTPEmail(userId: string) {
|
||||||
code: string,
|
|
||||||
): Promise<VerifyMyAuthFactorOTPResponse> {
|
|
||||||
const authService = auth.getAuth(server);
|
|
||||||
return authService.verifyMyAuthFactorOTP({ code }, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function addOTPEmail(
|
|
||||||
userId: string,
|
|
||||||
): Promise<AddOTPEmailResponse | undefined> {
|
|
||||||
const userService = user.getUser(server);
|
|
||||||
return userService.addOTPEmail(
|
return userService.addOTPEmail(
|
||||||
{
|
{
|
||||||
userId,
|
userId,
|
||||||
@@ -112,98 +54,72 @@ export async function addOTPEmail(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function addOTPSMS(
|
export async function addOTPSMS(userId: string, token?: string) {
|
||||||
userId: string,
|
// TODO: Follow up here, I do not understand the branching
|
||||||
token?: string,
|
// let userService;
|
||||||
): Promise<AddOTPSMSResponse | undefined> {
|
// if (token) {
|
||||||
let userService;
|
// const authConfig: ZitadelServerOptions = {
|
||||||
if (token) {
|
// name: "zitadel login",
|
||||||
const authConfig: ZitadelServerOptions = {
|
// apiUrl: process.env.ZITADEL_API_URL ?? "",
|
||||||
name: "zitadel login",
|
// token: token,
|
||||||
apiUrl: process.env.ZITADEL_API_URL ?? "",
|
// };
|
||||||
token: token,
|
// const sessionUser = initializeServer(authConfig);
|
||||||
};
|
// userService = user.getUser(sessionUser);
|
||||||
|
// } else {
|
||||||
|
// userService = user.getUser(server);
|
||||||
|
// }
|
||||||
|
|
||||||
const sessionUser = initializeServer(authConfig);
|
|
||||||
userService = user.getUser(sessionUser);
|
|
||||||
} else {
|
|
||||||
userService = user.getUser(server);
|
|
||||||
}
|
|
||||||
return userService.addOTPSMS({ userId }, {});
|
return userService.addOTPSMS({ userId }, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function registerTOTP(
|
export async function registerTOTP(userId: string, token?: string) {
|
||||||
userId: string,
|
// TODO: Follow up here, I do not understand the branching
|
||||||
token?: string,
|
// let userService;
|
||||||
): Promise<RegisterTOTPResponse | undefined> {
|
// if (token) {
|
||||||
let userService;
|
// const authConfig: ZitadelServerOptions = {
|
||||||
if (token) {
|
// name: "zitadel login",
|
||||||
const authConfig: ZitadelServerOptions = {
|
// apiUrl: process.env.ZITADEL_API_URL ?? "",
|
||||||
name: "zitadel login",
|
// token: token,
|
||||||
apiUrl: process.env.ZITADEL_API_URL ?? "",
|
// };
|
||||||
token: token,
|
//
|
||||||
};
|
// const sessionUser = initializeServer(authConfig);
|
||||||
|
// userService = user.getUser(sessionUser);
|
||||||
const sessionUser = initializeServer(authConfig);
|
// } else {
|
||||||
userService = user.getUser(sessionUser);
|
// userService = user.getUser(server);
|
||||||
} else {
|
// }
|
||||||
userService = user.getUser(server);
|
|
||||||
}
|
|
||||||
return userService.registerTOTP({ userId }, {});
|
return userService.registerTOTP({ userId }, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getGeneralSettings(
|
export async function getGeneralSettings() {
|
||||||
server: ZitadelServer,
|
|
||||||
): Promise<string[] | undefined> {
|
|
||||||
const settingsService = settings.getSettings(server);
|
|
||||||
return settingsService
|
return settingsService
|
||||||
.getGeneralSettings({}, {})
|
.getGeneralSettings({}, {})
|
||||||
.then((resp: GetGeneralSettingsResponse) => resp.supportedLanguages);
|
.then((resp) => resp.supportedLanguages);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getLegalAndSupportSettings(
|
export async function getLegalAndSupportSettings(organization?: string) {
|
||||||
server: ZitadelServer,
|
|
||||||
organization?: string,
|
|
||||||
): Promise<LegalAndSupportSettings | undefined> {
|
|
||||||
const settingsService = settings.getSettings(server);
|
|
||||||
return settingsService
|
return settingsService
|
||||||
.getLegalAndSupportSettings(
|
.getLegalAndSupportSettings({ ctx: makeReqCtx(organization) }, {})
|
||||||
{ ctx: organization ? { orgId: organization } : { instance: true } },
|
.then((resp) => {
|
||||||
{},
|
|
||||||
)
|
|
||||||
.then((resp: GetLegalAndSupportSettingsResponse) => {
|
|
||||||
return resp.settings;
|
return resp.settings;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getPasswordComplexitySettings(
|
export async function getPasswordComplexitySettings(organization?: string) {
|
||||||
server: ZitadelServer,
|
|
||||||
organization?: string,
|
|
||||||
): Promise<PasswordComplexitySettings | undefined> {
|
|
||||||
const settingsService = settings.getSettings(server);
|
|
||||||
|
|
||||||
return settingsService
|
return settingsService
|
||||||
.getPasswordComplexitySettings(
|
.getPasswordComplexitySettings({ ctx: makeReqCtx(organization) })
|
||||||
organization
|
.then((resp) => resp.settings);
|
||||||
? { ctx: { orgId: organization } }
|
|
||||||
: { ctx: { instance: true } },
|
|
||||||
{},
|
|
||||||
)
|
|
||||||
.then((resp: GetPasswordComplexitySettingsResponse) => resp.settings);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createSessionFromChecks(
|
export async function createSessionFromChecks(
|
||||||
server: ZitadelServer,
|
checks: PlainMessage<Checks>,
|
||||||
checks: Checks,
|
challenges: PlainMessage<RequestChallenges> | undefined,
|
||||||
challenges: RequestChallenges | undefined,
|
) {
|
||||||
): Promise<CreateSessionResponse | undefined> {
|
|
||||||
const sessionService = session.getSession(server);
|
|
||||||
return sessionService.createSession(
|
return sessionService.createSession(
|
||||||
{
|
{
|
||||||
checks: checks,
|
checks: checks,
|
||||||
challenges,
|
challenges,
|
||||||
lifetime: {
|
lifetime: {
|
||||||
seconds: SESSION_LIFETIME_S,
|
seconds: BigInt(SESSION_LIFETIME_S),
|
||||||
nanos: 0,
|
nanos: 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -212,77 +128,69 @@ export async function createSessionFromChecks(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function createSessionForUserIdAndIdpIntent(
|
export async function createSessionForUserIdAndIdpIntent(
|
||||||
server: ZitadelServer,
|
|
||||||
userId: string,
|
userId: string,
|
||||||
idpIntent: {
|
idpIntent: {
|
||||||
idpIntentId?: string | undefined;
|
idpIntentId?: string | undefined;
|
||||||
idpIntentToken?: string | undefined;
|
idpIntentToken?: string | undefined;
|
||||||
},
|
},
|
||||||
): Promise<CreateSessionResponse | undefined> {
|
) {
|
||||||
const sessionService = session.getSession(server);
|
return sessionService.createSession({
|
||||||
|
checks: {
|
||||||
|
user: {
|
||||||
|
search: {
|
||||||
|
case: "userId",
|
||||||
|
value: userId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
idpIntent,
|
||||||
|
},
|
||||||
|
// lifetime: {
|
||||||
|
// seconds: 300,
|
||||||
|
// nanos: 0,
|
||||||
|
// },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return sessionService.createSession(
|
export async function setSession(
|
||||||
|
sessionId: string,
|
||||||
|
sessionToken: string,
|
||||||
|
challenges: RequestChallenges | undefined,
|
||||||
|
checks?: PlainMessage<Checks>,
|
||||||
|
) {
|
||||||
|
return sessionService.setSession(
|
||||||
{
|
{
|
||||||
checks: { user: { userId }, idpIntent },
|
sessionId,
|
||||||
// lifetime: {
|
sessionToken,
|
||||||
// seconds: 300,
|
challenges,
|
||||||
// nanos: 0,
|
checks: checks ? checks : {},
|
||||||
// },
|
metadata: {},
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setSession(
|
export async function getSession(sessionId: string, sessionToken: string) {
|
||||||
server: ZitadelServer,
|
|
||||||
sessionId: string,
|
|
||||||
sessionToken: string,
|
|
||||||
challenges: RequestChallenges | undefined,
|
|
||||||
checks: Checks,
|
|
||||||
): Promise<SetSessionResponse | undefined> {
|
|
||||||
const sessionService = session.getSession(server);
|
|
||||||
|
|
||||||
const payload: SetSessionRequest = {
|
|
||||||
sessionId,
|
|
||||||
sessionToken,
|
|
||||||
challenges,
|
|
||||||
checks: {},
|
|
||||||
metadata: {},
|
|
||||||
};
|
|
||||||
|
|
||||||
if (checks && payload.checks) {
|
|
||||||
payload.checks = checks;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sessionService.setSession(payload, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getSession(
|
|
||||||
server: ZitadelServer,
|
|
||||||
sessionId: string,
|
|
||||||
sessionToken: string,
|
|
||||||
): Promise<GetSessionResponse | undefined> {
|
|
||||||
const sessionService = session.getSession(server);
|
|
||||||
return sessionService.getSession({ sessionId, sessionToken }, {});
|
return sessionService.getSession({ sessionId, sessionToken }, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function deleteSession(
|
export async function deleteSession(sessionId: string, sessionToken: string) {
|
||||||
server: ZitadelServer,
|
|
||||||
sessionId: string,
|
|
||||||
sessionToken: string,
|
|
||||||
): Promise<DeleteSessionResponse | undefined> {
|
|
||||||
const sessionService = session.getSession(server);
|
|
||||||
return sessionService.deleteSession({ sessionId, sessionToken }, {});
|
return sessionService.deleteSession({ sessionId, sessionToken }, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function listSessions(
|
export async function listSessions(ids: string[]) {
|
||||||
server: ZitadelServer,
|
return sessionService.listSessions(
|
||||||
ids: string[],
|
{
|
||||||
): Promise<ListSessionsResponse | undefined> {
|
queries: [
|
||||||
const sessionService = session.getSession(server);
|
{
|
||||||
const query = { offset: 0, limit: 100, asc: true };
|
query: {
|
||||||
const queries = [{ idsQuery: { ids } }];
|
case: "idsQuery",
|
||||||
return sessionService.listSessions({ queries: queries }, {});
|
value: { ids: ids },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AddHumanUserData = {
|
export type AddHumanUserData = {
|
||||||
@@ -293,89 +201,82 @@ export type AddHumanUserData = {
|
|||||||
organization: string | undefined;
|
organization: string | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function addHumanUser(
|
export async function addHumanUser({
|
||||||
server: ZitadelServer,
|
email,
|
||||||
{ email, firstName, lastName, password, organization }: AddHumanUserData,
|
firstName,
|
||||||
): Promise<AddHumanUserResponse> {
|
lastName,
|
||||||
const userService = user.getUser(server);
|
password,
|
||||||
|
organization,
|
||||||
const payload: Partial<AddHumanUserRequest> = {
|
}: AddHumanUserData) {
|
||||||
|
return userService.addHumanUser({
|
||||||
email: { email },
|
email: { email },
|
||||||
username: email,
|
username: email,
|
||||||
profile: { givenName: firstName, familyName: lastName },
|
profile: { givenName: firstName, familyName: lastName },
|
||||||
};
|
organization: organization
|
||||||
|
? { org: { case: "orgId", value: organization } }
|
||||||
if (organization) {
|
: undefined,
|
||||||
payload.organization = { orgId: organization };
|
passwordType: password
|
||||||
}
|
? { case: "password", value: { password: password } }
|
||||||
|
: undefined,
|
||||||
return userService.addHumanUser(
|
});
|
||||||
password
|
|
||||||
? {
|
|
||||||
...payload,
|
|
||||||
password: { password },
|
|
||||||
}
|
|
||||||
: payload,
|
|
||||||
{},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function verifyTOTPRegistration(
|
export async function verifyTOTPRegistration(
|
||||||
code: string,
|
code: string,
|
||||||
userId: string,
|
userId: string,
|
||||||
token?: string,
|
token?: string,
|
||||||
): Promise<VerifyTOTPRegistrationResponse> {
|
) {
|
||||||
let userService;
|
// let userService;
|
||||||
if (token) {
|
// if (token) {
|
||||||
const authConfig: ZitadelServerOptions = {
|
// const authConfig: ZitadelServerOptions = {
|
||||||
name: "zitadel login",
|
// name: "zitadel login",
|
||||||
apiUrl: process.env.ZITADEL_API_URL ?? "",
|
// apiUrl: process.env.ZITADEL_API_URL ?? "",
|
||||||
token: token,
|
// token: token,
|
||||||
};
|
// };
|
||||||
|
//
|
||||||
const sessionUser = initializeServer(authConfig);
|
// const sessionUser = initializeServer(authConfig);
|
||||||
userService = user.getUser(sessionUser);
|
// userService = user.getUser(sessionUser);
|
||||||
} else {
|
// } else {
|
||||||
userService = user.getUser(server);
|
// userService = user.getUser(server);
|
||||||
}
|
// }
|
||||||
return userService.verifyTOTPRegistration({ code, userId }, {});
|
return userService.verifyTOTPRegistration({ code, userId }, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getUserByID(
|
export async function getUserByID(userId: string) {
|
||||||
userId: string,
|
|
||||||
): Promise<GetUserByIDResponse> {
|
|
||||||
const userService = user.getUser(server);
|
|
||||||
|
|
||||||
return userService.getUserByID({ userId }, {});
|
return userService.getUserByID({ userId }, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function listUsers(
|
export async function listUsers(userName: string, organizationId: string) {
|
||||||
userName: string,
|
|
||||||
organizationId: string,
|
|
||||||
): Promise<ListUsersResponse> {
|
|
||||||
const userService = user.getUser(server);
|
|
||||||
|
|
||||||
return userService.listUsers(
|
return userService.listUsers(
|
||||||
{
|
{
|
||||||
queries: organizationId
|
queries: organizationId
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
userNameQuery: {
|
query: {
|
||||||
userName,
|
case: "userNameQuery",
|
||||||
method: TextQueryMethod.TEXT_QUERY_METHOD_EQUALS,
|
value: {
|
||||||
|
userName,
|
||||||
|
method: TextQueryMethod.EQUALS,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
organizationIdQuery: {
|
query: {
|
||||||
organizationId,
|
case: "organizationIdQuery",
|
||||||
|
value: {
|
||||||
|
organizationId,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
: [
|
: [
|
||||||
{
|
{
|
||||||
userNameQuery: {
|
query: {
|
||||||
userName,
|
case: "userNameQuery",
|
||||||
method: TextQueryMethod.TEXT_QUERY_METHOD_EQUALS,
|
value: {
|
||||||
|
userName,
|
||||||
|
method: TextQueryMethod.EQUALS,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -384,64 +285,52 @@ export async function listUsers(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getOrgByDomain(
|
export async function getOrgByDomain(domain: string) {
|
||||||
domain: string,
|
return managementService.getOrgByDomainGlobal({ domain }, {});
|
||||||
): Promise<GetOrgByDomainGlobalResponse> {
|
|
||||||
const mgmtService = management.getManagement(server);
|
|
||||||
return mgmtService.getOrgByDomainGlobal({ domain }, {});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function startIdentityProviderFlow(
|
export async function startIdentityProviderFlow({
|
||||||
server: ZitadelServer,
|
idpId,
|
||||||
{ idpId, urls }: StartIdentityProviderIntentRequest,
|
urls,
|
||||||
): Promise<StartIdentityProviderIntentResponse> {
|
}: {
|
||||||
const userService = user.getUser(server);
|
idpId: string;
|
||||||
|
urls: PlainMessage<RedirectURLs>;
|
||||||
|
}) {
|
||||||
return userService.startIdentityProviderIntent({
|
return userService.startIdentityProviderIntent({
|
||||||
idpId,
|
idpId,
|
||||||
urls,
|
content: {
|
||||||
|
case: "urls",
|
||||||
|
value: urls,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function retrieveIdentityProviderInformation(
|
export async function retrieveIdentityProviderInformation({
|
||||||
server: ZitadelServer,
|
idpIntentId,
|
||||||
{ idpIntentId, idpIntentToken }: RetrieveIdentityProviderIntentRequest,
|
idpIntentToken,
|
||||||
): Promise<RetrieveIdentityProviderIntentResponse> {
|
}: RetrieveIdentityProviderIntentRequest) {
|
||||||
const userService = user.getUser(server);
|
|
||||||
|
|
||||||
return userService.retrieveIdentityProviderIntent({
|
return userService.retrieveIdentityProviderIntent({
|
||||||
idpIntentId,
|
idpIntentId,
|
||||||
idpIntentToken,
|
idpIntentToken,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getAuthRequest(
|
export async function getAuthRequest({
|
||||||
server: ZitadelServer,
|
authRequestId,
|
||||||
{ authRequestId }: GetAuthRequestRequest,
|
}: {
|
||||||
): Promise<GetAuthRequestResponse> {
|
authRequestId: string;
|
||||||
const oidcService = oidc.getOidc(server);
|
}) {
|
||||||
|
|
||||||
return oidcService.getAuthRequest({
|
return oidcService.getAuthRequest({
|
||||||
authRequestId,
|
authRequestId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createCallback(
|
export async function createCallback(req: PlainMessage<CreateCallbackRequest>) {
|
||||||
server: ZitadelServer,
|
|
||||||
req: CreateCallbackRequest,
|
|
||||||
): Promise<CreateCallbackResponse> {
|
|
||||||
const oidcService = oidc.getOidc(server);
|
|
||||||
|
|
||||||
return oidcService.createCallback(req);
|
return oidcService.createCallback(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function verifyEmail(
|
export async function verifyEmail(userId: string, verificationCode: string) {
|
||||||
server: ZitadelServer,
|
return userService.verifyEmail(
|
||||||
userId: string,
|
|
||||||
verificationCode: string,
|
|
||||||
): Promise<VerifyEmailResponse> {
|
|
||||||
const userservice = user.getUser(server);
|
|
||||||
return userservice.verifyEmail(
|
|
||||||
{
|
{
|
||||||
userId,
|
userId,
|
||||||
verificationCode,
|
verificationCode,
|
||||||
@@ -452,16 +341,11 @@ export async function verifyEmail(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param server
|
|
||||||
* @param userId the id of the user where the email should be set
|
* @param userId the id of the user where the email should be set
|
||||||
* @returns the newly set email
|
* @returns the newly set email
|
||||||
*/
|
*/
|
||||||
export async function setEmail(
|
export async function setEmail(userId: string) {
|
||||||
server: ZitadelServer,
|
return userService.setEmail(
|
||||||
userId: string,
|
|
||||||
): Promise<any> {
|
|
||||||
const userservice = user.getUser(server);
|
|
||||||
return userservice.setEmail(
|
|
||||||
{
|
{
|
||||||
userId,
|
userId,
|
||||||
},
|
},
|
||||||
@@ -471,48 +355,44 @@ export async function setEmail(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param server
|
|
||||||
* @param userId the id of the user where the email should be set
|
* @param userId the id of the user where the email should be set
|
||||||
* @returns the newly set email
|
* @returns the newly set email
|
||||||
*/
|
*/
|
||||||
export async function createPasskeyRegistrationLink(
|
export async function createPasskeyRegistrationLink(
|
||||||
userId: string,
|
userId: string,
|
||||||
token?: string,
|
token?: string,
|
||||||
): Promise<any> {
|
) {
|
||||||
let userService;
|
// let userService;
|
||||||
if (token) {
|
// if (token) {
|
||||||
const authConfig: ZitadelServerOptions = {
|
// const authConfig: ZitadelServerOptions = {
|
||||||
name: "zitadel login",
|
// name: "zitadel login",
|
||||||
apiUrl: process.env.ZITADEL_API_URL ?? "",
|
// apiUrl: process.env.ZITADEL_API_URL ?? "",
|
||||||
token: token,
|
// token: token,
|
||||||
};
|
// };
|
||||||
|
//
|
||||||
const sessionUser = initializeServer(authConfig);
|
// const sessionUser = initializeServer(authConfig);
|
||||||
userService = user.getUser(sessionUser);
|
// userService = user.getUser(sessionUser);
|
||||||
} else {
|
// } else {
|
||||||
userService = user.getUser(server);
|
// userService = user.getUser(server);
|
||||||
}
|
// }
|
||||||
|
|
||||||
return userService.createPasskeyRegistrationLink({
|
return userService.createPasskeyRegistrationLink({
|
||||||
userId,
|
userId,
|
||||||
returnCode: {},
|
medium: {
|
||||||
|
case: "returnCode",
|
||||||
|
value: {},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param server
|
|
||||||
* @param userId the id of the user where the email should be set
|
* @param userId the id of the user where the email should be set
|
||||||
* @param domain the domain on which the factor is registered
|
* @param domain the domain on which the factor is registered
|
||||||
* @returns the newly set email
|
* @returns the newly set email
|
||||||
*/
|
*/
|
||||||
export async function registerU2F(
|
export async function registerU2F(userId: string, domain: string) {
|
||||||
userId: string,
|
return userService.registerU2F({
|
||||||
domain: string,
|
|
||||||
): Promise<RegisterU2FResponse> {
|
|
||||||
const userservice = user.getUser(server);
|
|
||||||
|
|
||||||
return userservice.registerU2F({
|
|
||||||
userId,
|
userId,
|
||||||
domain,
|
domain,
|
||||||
});
|
});
|
||||||
@@ -520,27 +400,22 @@ export async function registerU2F(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param server
|
|
||||||
* @param userId the id of the user where the email should be set
|
* @param userId the id of the user where the email should be set
|
||||||
* @param domain the domain on which the factor is registered
|
* @param domain the domain on which the factor is registered
|
||||||
* @returns the newly set email
|
* @returns the newly set email
|
||||||
*/
|
*/
|
||||||
export async function verifyU2FRegistration(
|
export async function verifyU2FRegistration(
|
||||||
request: VerifyU2FRegistrationRequest,
|
request: PlainMessage<VerifyU2FRegistrationRequest>,
|
||||||
): Promise<any> {
|
) {
|
||||||
const userservice = user.getUser(server);
|
return userService.verifyU2FRegistration(request, {});
|
||||||
|
|
||||||
return userservice.verifyU2FRegistration(request, {});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param server
|
|
||||||
* @param userId the id of the user where the email should be set
|
* @param userId the id of the user where the email should be set
|
||||||
* @returns the newly set email
|
* @returns the newly set email
|
||||||
*/
|
*/
|
||||||
export async function verifyPasskeyRegistration(
|
export async function verifyPasskeyRegistration(
|
||||||
server: ZitadelServer,
|
|
||||||
passkeyId: string,
|
passkeyId: string,
|
||||||
passkeyName: string,
|
passkeyName: string,
|
||||||
publicKeyCredential:
|
publicKeyCredential:
|
||||||
@@ -549,9 +424,8 @@ export async function verifyPasskeyRegistration(
|
|||||||
}
|
}
|
||||||
| undefined,
|
| undefined,
|
||||||
userId: string,
|
userId: string,
|
||||||
): Promise<VerifyPasskeyRegistrationResponse> {
|
) {
|
||||||
const userservice = user.getUser(server);
|
return userService.verifyPasskeyRegistration(
|
||||||
return userservice.verifyPasskeyRegistration(
|
|
||||||
{
|
{
|
||||||
passkeyId,
|
passkeyId,
|
||||||
passkeyName,
|
passkeyName,
|
||||||
@@ -564,7 +438,6 @@ export async function verifyPasskeyRegistration(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param server
|
|
||||||
* @param userId the id of the user where the email should be set
|
* @param userId the id of the user where the email should be set
|
||||||
* @returns the newly set email
|
* @returns the newly set email
|
||||||
*/
|
*/
|
||||||
@@ -572,9 +445,8 @@ export async function registerPasskey(
|
|||||||
userId: string,
|
userId: string,
|
||||||
code: { id: string; code: string },
|
code: { id: string; code: string },
|
||||||
domain: string,
|
domain: string,
|
||||||
): Promise<any> {
|
) {
|
||||||
const userservice = user.getUser(server);
|
return userService.registerPasskey({
|
||||||
return userservice.registerPasskey({
|
|
||||||
userId,
|
userId,
|
||||||
code,
|
code,
|
||||||
domain,
|
domain,
|
||||||
@@ -584,17 +456,11 @@ export async function registerPasskey(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param server
|
|
||||||
* @param userId the id of the user where the email should be set
|
* @param userId the id of the user where the email should be set
|
||||||
* @returns the newly set email
|
* @returns the newly set email
|
||||||
*/
|
*/
|
||||||
export async function listAuthenticationMethodTypes(
|
export async function listAuthenticationMethodTypes(userId: string) {
|
||||||
userId: string,
|
return userService.listAuthenticationMethodTypes({
|
||||||
): Promise<ListAuthenticationMethodTypesResponse> {
|
|
||||||
const userservice = user.getUser(server);
|
|
||||||
return userservice.listAuthenticationMethodTypes({
|
|
||||||
userId,
|
userId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export { server };
|
|
||||||
|
|||||||
@@ -1,15 +1,11 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import {
|
|
||||||
AuthenticationMethodType,
|
|
||||||
LoginSettings,
|
|
||||||
login,
|
|
||||||
} from "@zitadel/server";
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { BadgeState, StateBadge } from "./StateBadge";
|
import { BadgeState, StateBadge } from "./StateBadge";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import { CheckIcon } from "@heroicons/react/24/outline";
|
import { CheckIcon } from "@heroicons/react/24/outline";
|
||||||
import { EMAIL, SMS, TOTP, U2F } from "./AuthMethods";
|
import { EMAIL, SMS, TOTP, U2F } from "./AuthMethods";
|
||||||
|
import { AuthenticationMethodType } from "@zitadel/proto/zitadel/user/v2beta/user_service_pb";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
loginName?: string;
|
loginName?: string;
|
||||||
@@ -46,10 +42,14 @@ export default function ChooseSecondFactor({
|
|||||||
{userMethods.map((method, i) => {
|
{userMethods.map((method, i) => {
|
||||||
return (
|
return (
|
||||||
<div key={"method-" + i}>
|
<div key={"method-" + i}>
|
||||||
{method === 4 && TOTP(false, "/otp/time-based?" + params)}
|
{method === AuthenticationMethodType.TOTP &&
|
||||||
{method === 5 && U2F(false, "/u2f?" + params)}
|
TOTP(false, "/otp/time-based?" + params)}
|
||||||
{method === 7 && EMAIL(false, "/otp/email?" + params)}
|
{method === AuthenticationMethodType.U2F &&
|
||||||
{method === 6 && SMS(false, "/otp/sms?" + params)}
|
U2F(false, "/u2f?" + params)}
|
||||||
|
{method === AuthenticationMethodType.OTP_EMAIL &&
|
||||||
|
EMAIL(false, "/otp/email?" + params)}
|
||||||
|
{method === AuthenticationMethodType.OTP_SMS &&
|
||||||
|
SMS(false, "/otp/sms?" + params)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { AuthenticationMethodType, LoginSettings } from "@zitadel/server";
|
|
||||||
import { EMAIL, SMS, TOTP, U2F } from "./AuthMethods";
|
import { EMAIL, SMS, TOTP, U2F } from "./AuthMethods";
|
||||||
|
import { LoginSettings } from "@zitadel/proto/zitadel/settings/v2beta/login_settings_pb";
|
||||||
|
import { AuthenticationMethodType } from "@zitadel/proto/zitadel/user/v2beta/user_service_pb";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
loginName?: string;
|
loginName?: string;
|
||||||
@@ -48,13 +49,25 @@ export default function ChooseSecondFactorToSetup({
|
|||||||
<div className="grid grid-cols-1 gap-5 w-full pt-4">
|
<div className="grid grid-cols-1 gap-5 w-full pt-4">
|
||||||
{loginSettings.secondFactors.map((factor, i) => {
|
{loginSettings.secondFactors.map((factor, i) => {
|
||||||
return factor === 1
|
return factor === 1
|
||||||
? TOTP(userMethods.includes(4), "/otp/time-based/set?" + params)
|
? TOTP(
|
||||||
|
userMethods.includes(AuthenticationMethodType.TOTP),
|
||||||
|
"/otp/time-based/set?" + params,
|
||||||
|
)
|
||||||
: factor === 2
|
: factor === 2
|
||||||
? U2F(userMethods.includes(5), "/u2f/set?" + params)
|
? U2F(
|
||||||
|
userMethods.includes(AuthenticationMethodType.U2F),
|
||||||
|
"/u2f/set?" + params,
|
||||||
|
)
|
||||||
: factor === 3 && emailVerified
|
: factor === 3 && emailVerified
|
||||||
? EMAIL(userMethods.includes(7), "/otp/email/set?" + params)
|
? EMAIL(
|
||||||
|
userMethods.includes(AuthenticationMethodType.OTP_EMAIL),
|
||||||
|
"/otp/email/set?" + params,
|
||||||
|
)
|
||||||
: factor === 4 && phoneVerified
|
: factor === 4 && phoneVerified
|
||||||
? SMS(userMethods.includes(6), "/otp/sms/set?" + params)
|
? SMS(
|
||||||
|
userMethods.includes(AuthenticationMethodType.OTP_SMS),
|
||||||
|
"/otp/sms/set?" + params,
|
||||||
|
)
|
||||||
: null;
|
: null;
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { BrandingSettings } from "@zitadel/server";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Logo } from "@/ui/Logo";
|
import { Logo } from "@/ui/Logo";
|
||||||
import ThemeWrapper from "./ThemeWrapper";
|
import ThemeWrapper from "./ThemeWrapper";
|
||||||
import { LayoutProviders } from "./LayoutProviders";
|
import { LayoutProviders } from "./LayoutProviders";
|
||||||
|
import { BrandingSettings } from "@zitadel/proto/zitadel/settings/v2beta/branding_settings_pb";
|
||||||
|
|
||||||
export default function DynamicTheme({
|
export default function DynamicTheme({
|
||||||
branding,
|
branding,
|
||||||
|
|||||||
@@ -6,10 +6,11 @@ import { coerceToArrayBuffer, coerceToBase64Url } from "@/utils/base64";
|
|||||||
import { Button, ButtonVariants } from "./Button";
|
import { Button, ButtonVariants } from "./Button";
|
||||||
import Alert, { AlertType } from "./Alert";
|
import Alert, { AlertType } from "./Alert";
|
||||||
import { Spinner } from "./Spinner";
|
import { Spinner } from "./Spinner";
|
||||||
import { Checks } from "@zitadel/server";
|
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { TextInput } from "./Input";
|
import { TextInput } from "./Input";
|
||||||
import { Challenges } from "@zitadel/server";
|
import { Checks } from "@zitadel/proto/zitadel/session/v2beta/session_service_pb";
|
||||||
|
import { PlainMessage } from "@zitadel/client2";
|
||||||
|
import { Challenges } from "@zitadel/proto/zitadel/session/v2beta/challenge_pb";
|
||||||
|
|
||||||
// either loginName or sessionId must be provided
|
// either loginName or sessionId must be provided
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -63,7 +64,7 @@ export default function LoginOTP({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
async function updateSessionForOTPChallenge() {
|
async function updateSessionForOTPChallenge() {
|
||||||
const challenges: Challenges = {};
|
const challenges: PlainMessage<Challenges> = {};
|
||||||
|
|
||||||
if (method === "email") {
|
if (method === "email") {
|
||||||
challenges.otpEmail = "";
|
challenges.otpEmail = "";
|
||||||
@@ -111,7 +112,7 @@ export default function LoginOTP({
|
|||||||
body.authRequestId = authRequestId;
|
body.authRequestId = authRequestId;
|
||||||
}
|
}
|
||||||
|
|
||||||
const checks: Checks = {};
|
const checks: PlainMessage<Checks> = {};
|
||||||
if (method === "sms") {
|
if (method === "sms") {
|
||||||
checks.otpSms = { code: values.code };
|
checks.otpSms = { code: values.code };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { coerceToArrayBuffer, coerceToBase64Url } from "@/utils/base64";
|
|||||||
import { Button, ButtonVariants } from "./Button";
|
import { Button, ButtonVariants } from "./Button";
|
||||||
import Alert from "./Alert";
|
import Alert from "./Alert";
|
||||||
import { Spinner } from "./Spinner";
|
import { Spinner } from "./Spinner";
|
||||||
import { Checks } from "@zitadel/server";
|
import { Checks } from "@zitadel/proto/zitadel/session/v2beta/session_service_pb";
|
||||||
|
|
||||||
// either loginName or sessionId must be provided
|
// either loginName or sessionId must be provided
|
||||||
type Props = {
|
type Props = {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {
|
|||||||
symbolValidator,
|
symbolValidator,
|
||||||
upperCaseValidator,
|
upperCaseValidator,
|
||||||
} from "@/utils/validators";
|
} from "@/utils/validators";
|
||||||
import { PasswordComplexitySettings } from "@zitadel/server";
|
import { PasswordComplexitySettings } from "@zitadel/proto/zitadel/settings/v2beta/password_settings_pb";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
passwordComplexitySettings: PasswordComplexitySettings;
|
passwordComplexitySettings: PasswordComplexitySettings;
|
||||||
@@ -68,7 +68,7 @@ export default function PasswordComplexity({
|
|||||||
<div className="flex flex-row items-center">
|
<div className="flex flex-row items-center">
|
||||||
{hasMinLength ? check : cross}
|
{hasMinLength ? check : cross}
|
||||||
<span className={desc}>
|
<span className={desc}>
|
||||||
Password length {passwordComplexitySettings.minLength}
|
Password length {passwordComplexitySettings.minLength.toString()}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -7,12 +7,9 @@ import { useForm } from "react-hook-form";
|
|||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { Spinner } from "./Spinner";
|
import { Spinner } from "./Spinner";
|
||||||
import Alert from "./Alert";
|
import Alert from "./Alert";
|
||||||
import {
|
import { LoginSettings } from "@zitadel/proto/zitadel/settings/v2beta/login_settings_pb";
|
||||||
LoginSettings,
|
import { Checks } from "@zitadel/proto/zitadel/session/v2beta/session_service_pb";
|
||||||
AuthFactor,
|
import { AuthenticationMethodType } from "@zitadel/proto/zitadel/user/v2beta/user_service_pb";
|
||||||
Checks,
|
|
||||||
AuthenticationMethodType,
|
|
||||||
} from "@zitadel/server";
|
|
||||||
|
|
||||||
type Inputs = {
|
type Inputs = {
|
||||||
password: string;
|
password: string;
|
||||||
@@ -83,7 +80,8 @@ export default function PasswordForm({
|
|||||||
|
|
||||||
// exclude password
|
// exclude password
|
||||||
const availableSecondFactors = resp.authMethods?.filter(
|
const availableSecondFactors = resp.authMethods?.filter(
|
||||||
(m: AuthenticationMethodType) => m !== 1,
|
(m: AuthenticationMethodType) =>
|
||||||
|
m !== AuthenticationMethodType.PASSWORD,
|
||||||
);
|
);
|
||||||
if (availableSecondFactors.length == 1) {
|
if (availableSecondFactors.length == 1) {
|
||||||
const params = new URLSearchParams({
|
const params = new URLSearchParams({
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { Checkbox } from "./Checkbox";
|
import { Checkbox } from "./Checkbox";
|
||||||
import { LegalAndSupportSettings } from "@zitadel/server";
|
import { LegalAndSupportSettings } from "@zitadel/proto/zitadel/settings/v2beta/legal_settings_pb";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
legal: LegalAndSupportSettings;
|
legal: LegalAndSupportSettings;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { LegalAndSupportSettings } from "@zitadel/server";
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Button, ButtonVariants } from "./Button";
|
import { Button, ButtonVariants } from "./Button";
|
||||||
import { TextInput } from "./Input";
|
import { TextInput } from "./Input";
|
||||||
@@ -12,6 +11,7 @@ import AuthenticationMethodRadio, {
|
|||||||
methods,
|
methods,
|
||||||
} from "./AuthenticationMethodRadio";
|
} from "./AuthenticationMethodRadio";
|
||||||
import Alert from "./Alert";
|
import Alert from "./Alert";
|
||||||
|
import { LegalAndSupportSettings } from "@zitadel/proto/zitadel/settings/v2beta/legal_settings_pb";
|
||||||
|
|
||||||
type Inputs =
|
type Inputs =
|
||||||
| {
|
| {
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import { useForm } from "react-hook-form";
|
|||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { Spinner } from "./Spinner";
|
import { Spinner } from "./Spinner";
|
||||||
import Alert from "./Alert";
|
import Alert from "./Alert";
|
||||||
import { AuthRequest, RegisterPasskeyResponse } from "@zitadel/server";
|
|
||||||
import { coerceToArrayBuffer, coerceToBase64Url } from "@/utils/base64";
|
import { coerceToArrayBuffer, coerceToBase64Url } from "@/utils/base64";
|
||||||
|
import { RegisterPasskeyResponse } from "@zitadel/proto/zitadel/user/v2beta/user_service_pb";
|
||||||
type Inputs = {};
|
type Inputs = {};
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -89,37 +89,31 @@ export default function RegisterPasskey({
|
|||||||
function submitRegisterAndContinue(value: Inputs): Promise<boolean | void> {
|
function submitRegisterAndContinue(value: Inputs): Promise<boolean | void> {
|
||||||
return submitRegister().then((resp: RegisterPasskeyResponse) => {
|
return submitRegister().then((resp: RegisterPasskeyResponse) => {
|
||||||
const passkeyId = resp.passkeyId;
|
const passkeyId = resp.passkeyId;
|
||||||
|
const options: CredentialCreationOptions =
|
||||||
|
(resp.publicKeyCredentialCreationOptions?.toJson() as CredentialCreationOptions) ??
|
||||||
|
{};
|
||||||
|
|
||||||
if (
|
if (options?.publicKey) {
|
||||||
resp.publicKeyCredentialCreationOptions &&
|
options.publicKey.challenge = coerceToArrayBuffer(
|
||||||
resp.publicKeyCredentialCreationOptions.publicKey
|
options.publicKey.challenge,
|
||||||
) {
|
"challenge",
|
||||||
resp.publicKeyCredentialCreationOptions.publicKey.challenge =
|
);
|
||||||
coerceToArrayBuffer(
|
options.publicKey.user.id = coerceToArrayBuffer(
|
||||||
resp.publicKeyCredentialCreationOptions.publicKey.challenge,
|
options.publicKey.user.id,
|
||||||
"challenge",
|
"userid",
|
||||||
);
|
);
|
||||||
resp.publicKeyCredentialCreationOptions.publicKey.user.id =
|
if (options.publicKey.excludeCredentials) {
|
||||||
coerceToArrayBuffer(
|
options.publicKey.excludeCredentials.map((cred: any) => {
|
||||||
resp.publicKeyCredentialCreationOptions.publicKey.user.id,
|
cred.id = coerceToArrayBuffer(
|
||||||
"userid",
|
cred.id as string,
|
||||||
);
|
"excludeCredentials.id",
|
||||||
if (
|
);
|
||||||
resp.publicKeyCredentialCreationOptions.publicKey.excludeCredentials
|
return cred;
|
||||||
) {
|
});
|
||||||
resp.publicKeyCredentialCreationOptions.publicKey.excludeCredentials.map(
|
|
||||||
(cred: any) => {
|
|
||||||
cred.id = coerceToArrayBuffer(
|
|
||||||
cred.id as string,
|
|
||||||
"excludeCredentials.id",
|
|
||||||
);
|
|
||||||
return cred;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
navigator.credentials
|
navigator.credentials
|
||||||
.create(resp.publicKeyCredentialCreationOptions)
|
.create(options)
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
if (
|
if (
|
||||||
resp &&
|
resp &&
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import { useForm } from "react-hook-form";
|
|||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { Spinner } from "./Spinner";
|
import { Spinner } from "./Spinner";
|
||||||
import Alert from "./Alert";
|
import Alert from "./Alert";
|
||||||
import { RegisterU2FResponse } from "@zitadel/server";
|
|
||||||
import { coerceToArrayBuffer, coerceToBase64Url } from "@/utils/base64";
|
import { coerceToArrayBuffer, coerceToBase64Url } from "@/utils/base64";
|
||||||
|
import { RegisterU2FResponse } from "@zitadel/proto/zitadel/user/v2beta/user_service_pb";
|
||||||
type Inputs = {};
|
type Inputs = {};
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -87,37 +87,31 @@ export default function RegisterU2F({
|
|||||||
function submitRegisterAndContinue(value: Inputs): Promise<boolean | void> {
|
function submitRegisterAndContinue(value: Inputs): Promise<boolean | void> {
|
||||||
return submitRegister().then((resp: RegisterU2FResponse) => {
|
return submitRegister().then((resp: RegisterU2FResponse) => {
|
||||||
const u2fId = resp.u2fId;
|
const u2fId = resp.u2fId;
|
||||||
|
const options: CredentialCreationOptions =
|
||||||
|
(resp.publicKeyCredentialCreationOptions?.toJson() as CredentialCreationOptions) ??
|
||||||
|
{};
|
||||||
|
|
||||||
if (
|
if (options.publicKey) {
|
||||||
resp.publicKeyCredentialCreationOptions &&
|
options.publicKey.challenge = coerceToArrayBuffer(
|
||||||
resp.publicKeyCredentialCreationOptions.publicKey
|
options.publicKey.challenge,
|
||||||
) {
|
"challenge",
|
||||||
resp.publicKeyCredentialCreationOptions.publicKey.challenge =
|
);
|
||||||
coerceToArrayBuffer(
|
options.publicKey.user.id = coerceToArrayBuffer(
|
||||||
resp.publicKeyCredentialCreationOptions.publicKey.challenge,
|
options.publicKey.user.id,
|
||||||
"challenge",
|
"userid",
|
||||||
);
|
);
|
||||||
resp.publicKeyCredentialCreationOptions.publicKey.user.id =
|
if (options.publicKey.excludeCredentials) {
|
||||||
coerceToArrayBuffer(
|
options.publicKey.excludeCredentials.map((cred: any) => {
|
||||||
resp.publicKeyCredentialCreationOptions.publicKey.user.id,
|
cred.id = coerceToArrayBuffer(
|
||||||
"userid",
|
cred.id as string,
|
||||||
);
|
"excludeCredentials.id",
|
||||||
if (
|
);
|
||||||
resp.publicKeyCredentialCreationOptions.publicKey.excludeCredentials
|
return cred;
|
||||||
) {
|
});
|
||||||
resp.publicKeyCredentialCreationOptions.publicKey.excludeCredentials.map(
|
|
||||||
(cred: any) => {
|
|
||||||
cred.id = coerceToArrayBuffer(
|
|
||||||
cred.id as string,
|
|
||||||
"excludeCredentials.id",
|
|
||||||
);
|
|
||||||
return cred;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
navigator.credentials
|
navigator.credentials
|
||||||
.create(resp.publicKeyCredentialCreationOptions)
|
.create(options)
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
if (
|
if (
|
||||||
resp &&
|
resp &&
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import { Session } from "@zitadel/server";
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Avatar } from "./Avatar";
|
import { Avatar } from "./Avatar";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import { XCircleIcon } from "@heroicons/react/24/outline";
|
import { XCircleIcon } from "@heroicons/react/24/outline";
|
||||||
|
import { Session } from "@zitadel/proto/zitadel/session/v2beta/session_pb";
|
||||||
|
|
||||||
export default function SessionItem({
|
export default function SessionItem({
|
||||||
session,
|
session,
|
||||||
@@ -15,6 +16,8 @@ export default function SessionItem({
|
|||||||
reload: () => void;
|
reload: () => void;
|
||||||
authRequestId?: string;
|
authRequestId?: string;
|
||||||
}) {
|
}) {
|
||||||
|
// TODO: remove casting when bufbuild/protobuf-es@v2 is released
|
||||||
|
session = Session.fromJson(session as any);
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
|
|
||||||
async function clearSession(id: string) {
|
async function clearSession(id: string) {
|
||||||
@@ -43,7 +46,7 @@ export default function SessionItem({
|
|||||||
const validPassword = session?.factors?.password?.verifiedAt;
|
const validPassword = session?.factors?.password?.verifiedAt;
|
||||||
const validPasskey = session?.factors?.webAuthN?.verifiedAt;
|
const validPasskey = session?.factors?.webAuthN?.verifiedAt;
|
||||||
const stillValid = session.expirationDate
|
const stillValid = session.expirationDate
|
||||||
? session.expirationDate > new Date()
|
? session.expirationDate.toDate() > new Date()
|
||||||
: true;
|
: true;
|
||||||
|
|
||||||
const validDate = validPassword || validPasskey;
|
const validDate = validPassword || validPasskey;
|
||||||
@@ -96,7 +99,7 @@ export default function SessionItem({
|
|||||||
</span>
|
</span>
|
||||||
{validUser && (
|
{validUser && (
|
||||||
<span className="text-xs opacity-80">
|
<span className="text-xs opacity-80">
|
||||||
{validDate && moment(new Date(validDate)).fromNow()}
|
{validDate && moment(validDate.toDate()).fromNow()}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { Session } from "@zitadel/server";
|
|
||||||
import SessionItem from "./SessionItem";
|
import SessionItem from "./SessionItem";
|
||||||
import Alert from "./Alert";
|
import Alert from "./Alert";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
import { Session } from "@zitadel/proto/zitadel/session/v2beta/session_pb";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
sessions: Session[];
|
sessions: Session[];
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { PasswordComplexitySettings } from "@zitadel/server";
|
|
||||||
import PasswordComplexity from "./PasswordComplexity";
|
import PasswordComplexity from "./PasswordComplexity";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Button, ButtonVariants } from "./Button";
|
import { Button, ButtonVariants } from "./Button";
|
||||||
@@ -15,6 +14,7 @@ import {
|
|||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { Spinner } from "./Spinner";
|
import { Spinner } from "./Spinner";
|
||||||
import Alert from "./Alert";
|
import Alert from "./Alert";
|
||||||
|
import { PasswordComplexitySettings } from "@zitadel/proto/zitadel/settings/v2beta/password_settings_pb";
|
||||||
|
|
||||||
type Inputs =
|
type Inputs =
|
||||||
| {
|
| {
|
||||||
|
|||||||
@@ -10,11 +10,12 @@ import {
|
|||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { ProviderSlug } from "@/lib/demos";
|
import { ProviderSlug } from "@/lib/demos";
|
||||||
import Alert from "./Alert";
|
import Alert from "./Alert";
|
||||||
|
import { IdentityProvider } from "@zitadel/proto/zitadel/settings/v2beta/login_settings_pb";
|
||||||
|
|
||||||
export interface SignInWithIDPProps {
|
export interface SignInWithIDPProps {
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
host: string;
|
host: string;
|
||||||
identityProviders: any[];
|
identityProviders: IdentityProvider[];
|
||||||
authRequestId?: string;
|
authRequestId?: string;
|
||||||
organization?: string;
|
organization?: string;
|
||||||
startIDPFlowPath?: (idpId: string) => string;
|
startIDPFlowPath?: (idpId: string) => string;
|
||||||
@@ -30,6 +31,11 @@ export function SignInWithIDP({
|
|||||||
organization,
|
organization,
|
||||||
startIDPFlowPath = START_IDP_FLOW_PATH,
|
startIDPFlowPath = START_IDP_FLOW_PATH,
|
||||||
}: SignInWithIDPProps) {
|
}: SignInWithIDPProps) {
|
||||||
|
// TODO: remove casting when bufbuild/protobuf-es@v2 is released
|
||||||
|
identityProviders = identityProviders.map((idp) =>
|
||||||
|
IdentityProvider.fromJson(idp as any),
|
||||||
|
);
|
||||||
|
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
const [error, setError] = useState<string>("");
|
const [error, setError] = useState<string>("");
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import { useState } from "react";
|
|||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { verifyTOTP } from "@/lib/server-actions";
|
import { verifyTOTP } from "@/lib/server-actions";
|
||||||
import { login } from "@zitadel/server";
|
|
||||||
|
|
||||||
type Inputs = {
|
type Inputs = {
|
||||||
code: string;
|
code: string;
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { BrandingSettings } from "@zitadel/server";
|
|
||||||
import { setTheme } from "@/utils/colors";
|
import { setTheme } from "@/utils/colors";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
|
import { BrandingSettings } from "@zitadel/proto/zitadel/settings/v2beta/branding_settings_pb";
|
||||||
|
import { PartialMessage } from "@zitadel/client2";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
branding: Partial<BrandingSettings> | undefined;
|
branding: PartialMessage<BrandingSettings> | undefined;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import { TextInput } from "./Input";
|
|||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { Spinner } from "./Spinner";
|
import { Spinner } from "./Spinner";
|
||||||
import { LoginSettings } from "@zitadel/server";
|
|
||||||
import Alert from "./Alert";
|
import Alert from "./Alert";
|
||||||
|
import { LoginSettings } from "@zitadel/proto/zitadel/settings/v2beta/login_settings_pb";
|
||||||
|
|
||||||
type Inputs = {
|
type Inputs = {
|
||||||
loginName: string;
|
loginName: string;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import tinycolor from "tinycolor2";
|
import tinycolor from "tinycolor2";
|
||||||
|
import { BrandingSettings } from "@zitadel/proto/zitadel/settings/v2beta/branding_settings_pb";
|
||||||
import { BrandingSettings } from "@zitadel/server";
|
import { PartialMessage } from "@zitadel/client2";
|
||||||
|
|
||||||
export interface Color {
|
export interface Color {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -69,7 +69,10 @@ type BrandingColors = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export function setTheme(document: any, policy?: Partial<BrandingSettings>) {
|
export function setTheme(
|
||||||
|
document: any,
|
||||||
|
policy?: PartialMessage<BrandingSettings>,
|
||||||
|
) {
|
||||||
const lP: BrandingColors = {
|
const lP: BrandingColors = {
|
||||||
lightTheme: {
|
lightTheme: {
|
||||||
backgroundColor: policy?.lightTheme?.backgroundColor || BACKGROUND,
|
backgroundColor: policy?.lightTheme?.backgroundColor || BACKGROUND,
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import {
|
|||||||
createSessionFromChecks,
|
createSessionFromChecks,
|
||||||
createSessionForUserIdAndIdpIntent,
|
createSessionForUserIdAndIdpIntent,
|
||||||
getSession,
|
getSession,
|
||||||
server,
|
|
||||||
setSession,
|
setSession,
|
||||||
} from "@/lib/zitadel";
|
} from "@/lib/zitadel";
|
||||||
import {
|
import {
|
||||||
@@ -13,11 +12,12 @@ import {
|
|||||||
updateSessionCookie,
|
updateSessionCookie,
|
||||||
} from "./cookies";
|
} from "./cookies";
|
||||||
import {
|
import {
|
||||||
Session,
|
|
||||||
Challenges,
|
Challenges,
|
||||||
RequestChallenges,
|
RequestChallenges,
|
||||||
Checks,
|
} from "@zitadel/proto/zitadel/session/v2beta/challenge_pb";
|
||||||
} from "@zitadel/server";
|
import { Session } from "@zitadel/proto/zitadel/session/v2beta/session_pb";
|
||||||
|
import { Checks } from "@zitadel/proto/zitadel/session/v2beta/session_service_pb";
|
||||||
|
import { PlainMessage } from "@zitadel/client2";
|
||||||
|
|
||||||
export async function createSessionAndUpdateCookie(
|
export async function createSessionAndUpdateCookie(
|
||||||
loginName: string,
|
loginName: string,
|
||||||
@@ -25,22 +25,20 @@ export async function createSessionAndUpdateCookie(
|
|||||||
challenges: RequestChallenges | undefined,
|
challenges: RequestChallenges | undefined,
|
||||||
organization?: string,
|
organization?: string,
|
||||||
authRequestId?: string,
|
authRequestId?: string,
|
||||||
): Promise<Session> {
|
) {
|
||||||
const createdSession = await createSessionFromChecks(
|
const createdSession = await createSessionFromChecks(
|
||||||
server,
|
|
||||||
password
|
password
|
||||||
? {
|
? {
|
||||||
user: { loginName },
|
user: { search: { case: "loginName", value: loginName } },
|
||||||
password: { password },
|
password: { password },
|
||||||
// totp: { code: totpCode },
|
// totp: { code: totpCode },
|
||||||
}
|
}
|
||||||
: { user: { loginName } },
|
: { user: { search: { case: "loginName", value: loginName } } },
|
||||||
challenges,
|
challenges,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (createdSession) {
|
if (createdSession) {
|
||||||
return getSession(
|
return getSession(
|
||||||
server,
|
|
||||||
createdSession.sessionId,
|
createdSession.sessionId,
|
||||||
createdSession.sessionToken,
|
createdSession.sessionToken,
|
||||||
).then((response) => {
|
).then((response) => {
|
||||||
@@ -48,9 +46,9 @@ export async function createSessionAndUpdateCookie(
|
|||||||
const sessionCookie: SessionCookie = {
|
const sessionCookie: SessionCookie = {
|
||||||
id: createdSession.sessionId,
|
id: createdSession.sessionId,
|
||||||
token: createdSession.sessionToken,
|
token: createdSession.sessionToken,
|
||||||
creationDate: `${response.session.creationDate?.getTime() ?? ""}`,
|
creationDate: `${response.session.creationDate?.toDate().getTime() ?? ""}`,
|
||||||
expirationDate: `${response.session.expirationDate?.getTime() ?? ""}`,
|
expirationDate: `${response.session.expirationDate?.toDate().getTime() ?? ""}`,
|
||||||
changeDate: `${response.session.changeDate?.getTime() ?? ""}`,
|
changeDate: `${response.session.changeDate?.toDate().getTime() ?? ""}`,
|
||||||
loginName: response.session.factors.user.loginName ?? "",
|
loginName: response.session.factors.user.loginName ?? "",
|
||||||
organization: response.session.factors.user.organizationId ?? "",
|
organization: response.session.factors.user.organizationId ?? "",
|
||||||
};
|
};
|
||||||
@@ -82,20 +80,18 @@ export async function createSessionForUserIdAndUpdateCookie(
|
|||||||
authRequestId: string | undefined,
|
authRequestId: string | undefined,
|
||||||
): Promise<Session> {
|
): Promise<Session> {
|
||||||
const createdSession = await createSessionFromChecks(
|
const createdSession = await createSessionFromChecks(
|
||||||
server,
|
|
||||||
password
|
password
|
||||||
? {
|
? {
|
||||||
user: { userId },
|
user: { search: { case: "userId", value: userId } },
|
||||||
password: { password },
|
password: { password },
|
||||||
// totp: { code: totpCode },
|
// totp: { code: totpCode },
|
||||||
}
|
}
|
||||||
: { user: { userId } },
|
: { user: { search: { case: "userId", value: userId } } },
|
||||||
challenges,
|
challenges,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (createdSession) {
|
if (createdSession) {
|
||||||
return getSession(
|
return getSession(
|
||||||
server,
|
|
||||||
createdSession.sessionId,
|
createdSession.sessionId,
|
||||||
createdSession.sessionToken,
|
createdSession.sessionToken,
|
||||||
).then((response) => {
|
).then((response) => {
|
||||||
@@ -103,9 +99,9 @@ export async function createSessionForUserIdAndUpdateCookie(
|
|||||||
const sessionCookie: SessionCookie = {
|
const sessionCookie: SessionCookie = {
|
||||||
id: createdSession.sessionId,
|
id: createdSession.sessionId,
|
||||||
token: createdSession.sessionToken,
|
token: createdSession.sessionToken,
|
||||||
creationDate: `${response.session.creationDate?.getTime() ?? ""}`,
|
creationDate: `${response.session.creationDate?.toDate().getTime() ?? ""}`,
|
||||||
expirationDate: `${response.session.expirationDate?.getTime() ?? ""}`,
|
expirationDate: `${response.session.expirationDate?.toDate().getTime() ?? ""}`,
|
||||||
changeDate: `${response.session.changeDate?.getTime() ?? ""}`,
|
changeDate: `${response.session.changeDate?.toDate().getTime() ?? ""}`,
|
||||||
loginName: response.session.factors.user.loginName ?? "",
|
loginName: response.session.factors.user.loginName ?? "",
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -140,14 +136,12 @@ export async function createSessionForIdpAndUpdateCookie(
|
|||||||
authRequestId: string | undefined,
|
authRequestId: string | undefined,
|
||||||
): Promise<Session> {
|
): Promise<Session> {
|
||||||
const createdSession = await createSessionForUserIdAndIdpIntent(
|
const createdSession = await createSessionForUserIdAndIdpIntent(
|
||||||
server,
|
|
||||||
userId,
|
userId,
|
||||||
idpIntent,
|
idpIntent,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (createdSession) {
|
if (createdSession) {
|
||||||
return getSession(
|
return getSession(
|
||||||
server,
|
|
||||||
createdSession.sessionId,
|
createdSession.sessionId,
|
||||||
createdSession.sessionToken,
|
createdSession.sessionToken,
|
||||||
).then((response) => {
|
).then((response) => {
|
||||||
@@ -155,9 +149,9 @@ export async function createSessionForIdpAndUpdateCookie(
|
|||||||
const sessionCookie: SessionCookie = {
|
const sessionCookie: SessionCookie = {
|
||||||
id: createdSession.sessionId,
|
id: createdSession.sessionId,
|
||||||
token: createdSession.sessionToken,
|
token: createdSession.sessionToken,
|
||||||
creationDate: `${response.session.creationDate?.getTime() ?? ""}`,
|
creationDate: `${response.session.creationDate?.toDate().getTime() ?? ""}`,
|
||||||
expirationDate: `${response.session.expirationDate?.getTime() ?? ""}`,
|
expirationDate: `${response.session.expirationDate?.toDate().getTime() ?? ""}`,
|
||||||
changeDate: `${response.session.changeDate?.getTime() ?? ""}`,
|
changeDate: `${response.session.changeDate?.toDate().getTime() ?? ""}`,
|
||||||
loginName: response.session.factors.user.loginName ?? "",
|
loginName: response.session.factors.user.loginName ?? "",
|
||||||
organization: response.session.factors.user.organizationId ?? "",
|
organization: response.session.factors.user.organizationId ?? "",
|
||||||
};
|
};
|
||||||
@@ -188,12 +182,11 @@ export type SessionWithChallenges = Session & {
|
|||||||
|
|
||||||
export async function setSessionAndUpdateCookie(
|
export async function setSessionAndUpdateCookie(
|
||||||
recentCookie: SessionCookie,
|
recentCookie: SessionCookie,
|
||||||
checks: Checks,
|
checks: PlainMessage<Checks>,
|
||||||
challenges: RequestChallenges | undefined,
|
challenges: RequestChallenges | undefined,
|
||||||
authRequestId: string | undefined,
|
authRequestId: string | undefined,
|
||||||
): Promise<SessionWithChallenges> {
|
) {
|
||||||
return setSession(
|
return setSession(
|
||||||
server,
|
|
||||||
recentCookie.id,
|
recentCookie.id,
|
||||||
recentCookie.token,
|
recentCookie.token,
|
||||||
challenges,
|
challenges,
|
||||||
@@ -205,7 +198,7 @@ export async function setSessionAndUpdateCookie(
|
|||||||
token: updatedSession.sessionToken,
|
token: updatedSession.sessionToken,
|
||||||
creationDate: recentCookie.creationDate,
|
creationDate: recentCookie.creationDate,
|
||||||
expirationDate: recentCookie.expirationDate,
|
expirationDate: recentCookie.expirationDate,
|
||||||
changeDate: `${updatedSession.details?.changeDate?.getTime() ?? ""}`,
|
changeDate: `${updatedSession.details?.changeDate?.toDate().getTime() ?? ""}`,
|
||||||
loginName: recentCookie.loginName,
|
loginName: recentCookie.loginName,
|
||||||
organization: recentCookie.organization,
|
organization: recentCookie.organization,
|
||||||
};
|
};
|
||||||
@@ -214,7 +207,7 @@ export async function setSessionAndUpdateCookie(
|
|||||||
sessionCookie.authRequestId = authRequestId;
|
sessionCookie.authRequestId = authRequestId;
|
||||||
}
|
}
|
||||||
|
|
||||||
return getSession(server, sessionCookie.id, sessionCookie.token).then(
|
return getSession(sessionCookie.id, sessionCookie.token).then(
|
||||||
(response) => {
|
(response) => {
|
||||||
if (response?.session && response.session.factors?.user?.loginName) {
|
if (response?.session && response.session.factors?.user?.loginName) {
|
||||||
const { session } = response;
|
const { session } = response;
|
||||||
@@ -223,7 +216,7 @@ export async function setSessionAndUpdateCookie(
|
|||||||
token: updatedSession.sessionToken,
|
token: updatedSession.sessionToken,
|
||||||
creationDate: sessionCookie.creationDate,
|
creationDate: sessionCookie.creationDate,
|
||||||
expirationDate: sessionCookie.expirationDate,
|
expirationDate: sessionCookie.expirationDate,
|
||||||
changeDate: `${session.changeDate?.getTime() ?? ""}`,
|
changeDate: `${session.changeDate?.toDate().getTime() ?? ""}`,
|
||||||
loginName: session.factors?.user?.loginName ?? "",
|
loginName: session.factors?.user?.loginName ?? "",
|
||||||
organization: session.factors?.user?.organizationId ?? "",
|
organization: session.factors?.user?.organizationId ?? "",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
"extends": "@zitadel/tsconfig/nextjs.json",
|
"extends": "@zitadel/tsconfig/nextjs.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"jsx": "preserve",
|
"jsx": "preserve",
|
||||||
"rootDir": ".",
|
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./src/*"]
|
"@/*": ["./src/*"]
|
||||||
|
|||||||
@@ -6,16 +6,32 @@
|
|||||||
"dependsOn": ["^build"]
|
"dependsOn": ["^build"]
|
||||||
},
|
},
|
||||||
"test": {
|
"test": {
|
||||||
"dependsOn": ["@zitadel/server#build", "@zitadel/react#build"]
|
"dependsOn": [
|
||||||
|
"@zitadel/node#build",
|
||||||
|
"@zitadel/client2#build",
|
||||||
|
"@zitadel/react#build"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"test:integration": {
|
"test:integration": {
|
||||||
"dependsOn": ["@zitadel/server#build", "@zitadel/react#build"]
|
"dependsOn": [
|
||||||
|
"@zitadel/node#build",
|
||||||
|
"@zitadel/client2#build",
|
||||||
|
"@zitadel/react#build"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"test:unit": {
|
"test:unit": {
|
||||||
"dependsOn": ["@zitadel/server#build"]
|
"dependsOn": [
|
||||||
|
"@zitadel/node#build",
|
||||||
|
"@zitadel/client2#build",
|
||||||
|
"@zitadel/react#build"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"test:watch": {
|
"test:watch": {
|
||||||
"dependsOn": ["@zitadel/server#build", "@zitadel/react#build"]
|
"dependsOn": [
|
||||||
|
"@zitadel/node#build",
|
||||||
|
"@zitadel/client2#build",
|
||||||
|
"@zitadel/react#build"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,14 +3,14 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"eslint-config-next": "^14.2.3",
|
"eslint-config-next": "^14.2.3",
|
||||||
"@typescript-eslint/parser": "^7.9.0",
|
"@typescript-eslint/parser": "^7.9.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-react": "^7.34.1",
|
"eslint-plugin-react": "^7.34.1",
|
||||||
"eslint-config-turbo": "^1.13.3"
|
"eslint-config-turbo": "^1.13.3"
|
||||||
},
|
|
||||||
"publishConfig": {
|
|
||||||
"access": "public"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,9 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"dist/**"
|
"dist/**"
|
||||||
],
|
],
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsup",
|
"build": "tsup",
|
||||||
"test": "pnpm test:unit",
|
"test": "pnpm test:unit",
|
||||||
@@ -19,25 +22,21 @@
|
|||||||
"lint": "eslint \"src/**/*.ts*\"",
|
"lint": "eslint \"src/**/*.ts*\"",
|
||||||
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist"
|
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist"
|
||||||
},
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@zitadel/react": "workspace:*",
|
||||||
|
"@zitadel/node": "workspace:*",
|
||||||
|
"next": "^14.2.3",
|
||||||
|
"react": "18.2.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"next": "^14.2.3"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/react": "^17.0.13",
|
"@types/react": "^17.0.13",
|
||||||
"@zitadel/tsconfig": "workspace:*",
|
"@zitadel/tsconfig": "workspace:*",
|
||||||
"eslint-config-zitadel": "workspace:*",
|
"eslint-config-zitadel": "workspace:*",
|
||||||
"tailwindcss": "3.2.4",
|
"tailwindcss": "3.2.4",
|
||||||
"postcss": "8.4.21",
|
"postcss": "8.4.21",
|
||||||
"zitadel-tailwind-config": "workspace:*",
|
"zitadel-tailwind-config": "workspace:*"
|
||||||
"@zitadel/server": "workspace:*"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@zitadel/react": "workspace:*",
|
|
||||||
"@zitadel/server": "workspace:*",
|
|
||||||
"next": "^14.2.3",
|
|
||||||
"react": "18.2.0"
|
|
||||||
},
|
|
||||||
"publishConfig": {
|
|
||||||
"access": "public"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"next": "^14.2.3"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
},
|
},
|
||||||
"build": {
|
"build": {
|
||||||
"outputs": ["dist/**"],
|
"outputs": ["dist/**"],
|
||||||
"dependsOn": ["@zitadel/react#build", "@zitadel/server#build"]
|
"dependsOn": ["@zitadel/react#build"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,9 +32,11 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@connectrpc/connect-node": "^1.4.0",
|
"@connectrpc/connect-node": "^1.4.0",
|
||||||
|
"@connectrpc/connect-web": "^1.4.0",
|
||||||
"jose": "^5.3.0"
|
"jose": "^5.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/node": "^20.14.2",
|
||||||
"@zitadel/client2": "workspace:*",
|
"@zitadel/client2": "workspace:*",
|
||||||
"@zitadel/tsconfig": "workspace:*",
|
"@zitadel/tsconfig": "workspace:*",
|
||||||
"eslint-config-zitadel": "workspace:*"
|
"eslint-config-zitadel": "workspace:*"
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { NewAuthorizationBearerInterceptor } from "@zitadel/client2";
|
import { NewAuthorizationBearerInterceptor } from "@zitadel/client2";
|
||||||
import {
|
// import {
|
||||||
createGrpcWebTransport,
|
// createGrpcWebTransport,
|
||||||
GrpcTransportOptions,
|
// GrpcWebTransportOptions,
|
||||||
} from "@connectrpc/connect-node";
|
// } from "@connectrpc/connect-node";
|
||||||
|
import { createGrpcWebTransport, GrpcWebTransportOptions } from "@connectrpc/connect-web";
|
||||||
import { importPKCS8, SignJWT } from "jose";
|
import { importPKCS8, SignJWT } from "jose";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -12,7 +13,7 @@ import { importPKCS8, SignJWT } from "jose";
|
|||||||
*/
|
*/
|
||||||
export function createServerTransport(
|
export function createServerTransport(
|
||||||
token: string,
|
token: string,
|
||||||
opts: GrpcTransportOptions,
|
opts: GrpcWebTransportOptions,
|
||||||
) {
|
) {
|
||||||
return createGrpcWebTransport({
|
return createGrpcWebTransport({
|
||||||
...opts,
|
...opts,
|
||||||
|
|||||||
@@ -14,6 +14,9 @@
|
|||||||
"./styles.css": "./dist/index.css",
|
"./styles.css": "./dist/index.css",
|
||||||
"./assets/*": "./dist/assets/*"
|
"./assets/*": "./dist/assets/*"
|
||||||
},
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsup",
|
"build": "tsup",
|
||||||
"test": "pnpm test:unit",
|
"test": "pnpm test:unit",
|
||||||
@@ -25,6 +28,9 @@
|
|||||||
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist",
|
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist",
|
||||||
"copy-files": "cp -R ./src/public/ ./dist/"
|
"copy-files": "cp -R ./src/public/ ./dist/"
|
||||||
},
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^18.2.0"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@testing-library/jest-dom": "^6.4.5",
|
"@testing-library/jest-dom": "^6.4.5",
|
||||||
"@testing-library/react": "^14.0.0",
|
"@testing-library/react": "^14.0.0",
|
||||||
@@ -38,11 +44,5 @@
|
|||||||
"sass": "^1.77.1",
|
"sass": "^1.77.1",
|
||||||
"tailwindcss": "3.2.4",
|
"tailwindcss": "3.2.4",
|
||||||
"zitadel-tailwind-config": "workspace:*"
|
"zitadel-tailwind-config": "workspace:*"
|
||||||
},
|
|
||||||
"publishConfig": {
|
|
||||||
"access": "public"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"react": "18.2.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
{
|
{
|
||||||
"extends": "@zitadel/tsconfig/react-library.json",
|
"extends": "@zitadel/tsconfig/react-library.json",
|
||||||
"include": ["."],
|
"include": ["."],
|
||||||
"exclude": ["dist", "build", "node_modules"]
|
"exclude": ["dist", "build", "node_modules"],
|
||||||
|
"compilerOptions": {
|
||||||
|
"types": ["@testing-library/jest-dom"]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
741
pnpm-lock.yaml
generated
741
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user