mirror of
https://github.com/zitadel/zitadel.git
synced 2025-12-12 10:25:58 +00:00
init idp flow from scope, org discovery register
This commit is contained in:
@@ -1,14 +1,7 @@
|
||||
import { ProviderSlug } from "@/lib/demos";
|
||||
import { getBrandingSettings } from "@/lib/zitadel";
|
||||
import { getBrandingSettings, PROVIDER_NAME_MAPPING } from "@/lib/zitadel";
|
||||
import DynamicTheme from "@/ui/DynamicTheme";
|
||||
|
||||
const PROVIDER_NAME_MAPPING: {
|
||||
[provider: string]: string;
|
||||
} = {
|
||||
[ProviderSlug.GOOGLE]: "Google",
|
||||
[ProviderSlug.GITHUB]: "GitHub",
|
||||
};
|
||||
|
||||
export default async function Page({
|
||||
searchParams,
|
||||
params,
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
import { ProviderSlug } from "@/lib/demos";
|
||||
import {
|
||||
getActiveIdentityProviders,
|
||||
getLoginSettings,
|
||||
listAuthenticationMethodTypes,
|
||||
listUsers,
|
||||
PROVIDER_NAME_MAPPING,
|
||||
startIdentityProviderFlow,
|
||||
} from "@/lib/zitadel";
|
||||
import { createSessionForUserIdAndUpdateCookie } from "@/utils/session";
|
||||
import { makeReqCtx } from "@zitadel/client2/v2beta";
|
||||
import { IdentityProviderType } from "@zitadel/proto/zitadel/settings/v2beta/login_settings_pb";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
@@ -43,8 +49,62 @@ export async function POST(request: NextRequest) {
|
||||
} else if (organization) {
|
||||
const loginSettings = await getLoginSettings(organization);
|
||||
|
||||
// TODO: check if allowDomainDiscovery has to be allowed too, to redirect to the register page
|
||||
// user not found, check if register is enabled on organization
|
||||
if (loginSettings?.allowRegister) {
|
||||
|
||||
if (
|
||||
loginSettings?.allowRegister &&
|
||||
!loginSettings?.allowUsernamePassword
|
||||
) {
|
||||
// TODO redirect to loginname page with idp hint
|
||||
const identityProviders = await getActiveIdentityProviders(
|
||||
organization,
|
||||
).then((resp) => {
|
||||
return resp.identityProviders;
|
||||
});
|
||||
|
||||
if (identityProviders.length === 1) {
|
||||
const host = request.nextUrl.origin;
|
||||
|
||||
const provider =
|
||||
identityProviders[0].type === IdentityProviderType.GITHUB
|
||||
? "github"
|
||||
: identityProviders[0].type === IdentityProviderType.GOOGLE
|
||||
? "google"
|
||||
: identityProviders[0].type === IdentityProviderType.AZURE_AD
|
||||
? "azure"
|
||||
: identityProviders[0].type === IdentityProviderType.SAML
|
||||
? "saml"
|
||||
: identityProviders[0].type === IdentityProviderType.OIDC
|
||||
? "oidc"
|
||||
: "oidc";
|
||||
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (authRequestId) {
|
||||
params.set("authRequestId", authRequestId);
|
||||
}
|
||||
|
||||
if (organization) {
|
||||
params.set("organization", organization);
|
||||
}
|
||||
|
||||
return startIdentityProviderFlow({
|
||||
idpId: identityProviders[0].id,
|
||||
urls: {
|
||||
successUrl:
|
||||
`${host}/idp/${provider}/success?` +
|
||||
new URLSearchParams(params),
|
||||
failureUrl:
|
||||
`${host}/idp/${provider}/failure?` +
|
||||
new URLSearchParams(params),
|
||||
},
|
||||
});
|
||||
}
|
||||
} else if (
|
||||
loginSettings?.allowRegister &&
|
||||
loginSettings?.allowUsernamePassword
|
||||
) {
|
||||
const params: any = { organization };
|
||||
if (authRequestId) {
|
||||
params.authRequestId = authRequestId;
|
||||
@@ -62,12 +122,12 @@ export async function POST(request: NextRequest) {
|
||||
nextUrl: registerUrl,
|
||||
status: 200,
|
||||
});
|
||||
} else {
|
||||
return NextResponse.json(
|
||||
{ message: "Could not find user" },
|
||||
{ status: 404 },
|
||||
);
|
||||
}
|
||||
|
||||
return NextResponse.json(
|
||||
{ message: "Could not find user" },
|
||||
{ status: 404 },
|
||||
);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import {
|
||||
createCallback,
|
||||
getActiveIdentityProviders,
|
||||
getAuthRequest,
|
||||
getOrgByDomain,
|
||||
listSessions,
|
||||
startIdentityProviderFlow,
|
||||
} from "@/lib/zitadel";
|
||||
import { SessionCookie, getAllSessions } from "@/utils/cookies";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
@@ -11,6 +13,7 @@ import {
|
||||
AuthRequest,
|
||||
Prompt,
|
||||
} from "@zitadel/proto/zitadel/oidc/v2beta/authorization_pb";
|
||||
import { IdentityProviderType } from "@zitadel/proto/zitadel/settings/v2beta/login_settings_pb";
|
||||
|
||||
async function loadSessions(ids: string[]): Promise<Session[]> {
|
||||
const response = await listSessions(
|
||||
@@ -22,6 +25,7 @@ async function loadSessions(ids: string[]): Promise<Session[]> {
|
||||
|
||||
const ORG_SCOPE_REGEX = /urn:zitadel:iam:org:id:([0-9]+)/;
|
||||
const ORG_DOMAIN_SCOPE_REGEX = /urn:zitadel:iam:org:domain:primary:(.+)/; // TODO: check regex for all domain character options
|
||||
const IDP_SCOPE_REGEX = /urn:zitadel:iam:org:idp:id:(.+)/;
|
||||
|
||||
function findSession(
|
||||
sessions: Session[],
|
||||
@@ -100,12 +104,17 @@ export async function GET(request: NextRequest) {
|
||||
const { authRequest } = await getAuthRequest({ authRequestId });
|
||||
|
||||
let organization = "";
|
||||
let idpId = "";
|
||||
|
||||
if (authRequest?.scope) {
|
||||
const orgScope = authRequest.scope.find((s: string) =>
|
||||
ORG_SCOPE_REGEX.test(s),
|
||||
);
|
||||
|
||||
const idpScope = authRequest.scope.find((s: string) =>
|
||||
IDP_SCOPE_REGEX.test(s),
|
||||
);
|
||||
|
||||
if (orgScope) {
|
||||
const matched = ORG_SCOPE_REGEX.exec(orgScope);
|
||||
organization = matched?.[1] ?? "";
|
||||
@@ -123,6 +132,58 @@ export async function GET(request: NextRequest) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (idpScope) {
|
||||
const matched = IDP_SCOPE_REGEX.exec(idpScope);
|
||||
idpId = matched?.[1] ?? "";
|
||||
|
||||
const identityProviders = await getActiveIdentityProviders(
|
||||
organization,
|
||||
).then((resp) => {
|
||||
return resp.identityProviders;
|
||||
});
|
||||
|
||||
const idp = identityProviders.find((idp) => idp.id === idpId);
|
||||
|
||||
if (idp) {
|
||||
const host = request.nextUrl.origin;
|
||||
|
||||
const provider =
|
||||
idp.type === IdentityProviderType.GITHUB
|
||||
? "github"
|
||||
: identityProviders[0].type === IdentityProviderType.GOOGLE
|
||||
? "google"
|
||||
: identityProviders[0].type === IdentityProviderType.AZURE_AD
|
||||
? "azure"
|
||||
: identityProviders[0].type === IdentityProviderType.SAML
|
||||
? "saml"
|
||||
: identityProviders[0].type === IdentityProviderType.OIDC
|
||||
? "oidc"
|
||||
: "oidc";
|
||||
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (authRequestId) {
|
||||
params.set("authRequestId", authRequestId);
|
||||
}
|
||||
|
||||
if (organization) {
|
||||
params.set("organization", organization);
|
||||
}
|
||||
|
||||
return startIdentityProviderFlow({
|
||||
idpId,
|
||||
urls: {
|
||||
successUrl:
|
||||
`${host}/idp/${provider}/success?` +
|
||||
new URLSearchParams(params),
|
||||
failureUrl:
|
||||
`${host}/idp/${provider}/failure?` +
|
||||
new URLSearchParams(params),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const gotoAccounts = (): NextResponse<unknown> => {
|
||||
|
||||
@@ -7,16 +7,19 @@ import {
|
||||
} from "@zitadel/client2/v2beta";
|
||||
import { createManagementServiceClient } from "@zitadel/client2/v1";
|
||||
import { createServerTransport } from "@zitadel/node";
|
||||
import { GetActiveIdentityProvidersRequest } from "@zitadel/proto/zitadel/settings/v2beta/settings_service_pb";
|
||||
import { Checks } from "@zitadel/proto/zitadel/session/v2beta/session_service_pb";
|
||||
import { RequestChallenges } from "@zitadel/proto/zitadel/session/v2beta/challenge_pb";
|
||||
import {
|
||||
RetrieveIdentityProviderIntentRequest,
|
||||
VerifyU2FRegistrationRequest,
|
||||
} from "@zitadel/proto/zitadel/user/v2beta/user_service_pb";
|
||||
|
||||
import { CreateCallbackRequest } from "@zitadel/proto/zitadel/oidc/v2beta/oidc_service_pb";
|
||||
import { TextQueryMethod } from "@zitadel/proto/zitadel/object/v2beta/object_pb";
|
||||
import type { RedirectURLs } from "@zitadel/proto/zitadel/user/v2beta/idp_pb";
|
||||
import { PlainMessage } from "@zitadel/client2";
|
||||
import { ProviderSlug } from "./demos";
|
||||
|
||||
const SESSION_LIFETIME_S = 3000;
|
||||
|
||||
@@ -290,6 +293,13 @@ export async function getOrgByDomain(domain: string) {
|
||||
return managementService.getOrgByDomainGlobal({ domain }, {});
|
||||
}
|
||||
|
||||
export const PROVIDER_NAME_MAPPING: {
|
||||
[provider: string]: string;
|
||||
} = {
|
||||
[ProviderSlug.GOOGLE]: "Google",
|
||||
[ProviderSlug.GITHUB]: "GitHub",
|
||||
};
|
||||
|
||||
export async function startIdentityProviderFlow({
|
||||
idpId,
|
||||
urls,
|
||||
@@ -426,6 +436,13 @@ export async function verifyU2FRegistration(
|
||||
return userService.verifyU2FRegistration(request, {});
|
||||
}
|
||||
|
||||
export async function getActiveIdentityProviders(orgId: string) {
|
||||
return settingsService.getActiveIdentityProviders(
|
||||
{ ctx: makeReqCtx(orgId) },
|
||||
{},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param userId the id of the user where the email should be set
|
||||
|
||||
@@ -19,18 +19,13 @@ export interface SignInWithIDPProps {
|
||||
identityProviders: IdentityProvider[];
|
||||
authRequestId?: string;
|
||||
organization?: string;
|
||||
startIDPFlowPath?: (idpId: string) => string;
|
||||
}
|
||||
|
||||
const START_IDP_FLOW_PATH = (idpId: string) =>
|
||||
`/v2beta/users/idps/${idpId}/start`;
|
||||
|
||||
export function SignInWithIDP({
|
||||
host,
|
||||
identityProviders,
|
||||
authRequestId,
|
||||
organization,
|
||||
startIDPFlowPath = START_IDP_FLOW_PATH,
|
||||
}: SignInWithIDPProps) {
|
||||
// TODO: remove casting when bufbuild/protobuf-es@v2 is released
|
||||
identityProviders = identityProviders.map((idp) =>
|
||||
|
||||
@@ -93,7 +93,8 @@ export default function UsernameForm({
|
||||
loginName: response.factors.user.loginName,
|
||||
};
|
||||
|
||||
// TODO: think about a method to skip org discovery from the session
|
||||
// TODO: does this have to be checked in loginSettings.allowDomainDiscovery
|
||||
|
||||
if (organization || response.factors.user.organizationId) {
|
||||
paramsPassword.organization =
|
||||
organization ?? response.factors.user.organizationId;
|
||||
|
||||
Reference in New Issue
Block a user