Skip to content

Commit

Permalink
Merge pull request #35 from zitadel/idp-register
Browse files Browse the repository at this point in the history
IDP register
  • Loading branch information
peintnermax authored Aug 3, 2023
2 parents 0954b93 + f1aeafc commit b330367
Show file tree
Hide file tree
Showing 53 changed files with 2,883 additions and 2,117 deletions.
44 changes: 0 additions & 44 deletions .github/workflows/release.yml

This file was deleted.

18 changes: 8 additions & 10 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,36 @@ on: pull_request

jobs:
quality:

name: Ensure Quality

runs-on: ubuntu-latest

timeout-minutes: 30

permissions:
contents: 'read'
contents: "read"

strategy:
fail-fast: false
matrix:
command:
- lint
- test:unit
- test:integration
- lint
- test:unit
- test:integration

steps:

- name: Checkout Repo
uses: actions/checkout@v2
uses: actions/checkout@v3

- name: Setup pnpm 7
uses: pnpm/action-setup@v2
with:
version: 7

- name: Setup Node.js 16.x
uses: actions/setup-node@v2
- name: Setup Node.js 18.x
uses: actions/setup-node@v3
with:
node-version: 16.x
node-version: 18.x

- uses: pnpm/action-setup@v2
name: Install pnpm
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ packages/zitadel-server/src/app/proto
packages/zitadel-client/src/app/proto
.vscode
.idea
.vercel
.env*.local
133 changes: 133 additions & 0 deletions apps/login/app/(login)/register/idp/[provider]/success/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import { ProviderSlug } from "#/lib/demos";
import { addHumanUser, server } from "#/lib/zitadel";
import Alert, { AlertType } from "#/ui/Alert";
import {
AddHumanUserRequest,
IDPInformation,
RetrieveIdentityProviderInformationResponse,
user,
IDPLink,
} from "@zitadel/server";

const PROVIDER_MAPPING: {
[provider: string]: (rI: IDPInformation) => Partial<AddHumanUserRequest>;
} = {
[ProviderSlug.GOOGLE]: (idp: IDPInformation) => {
const idpLink: IDPLink = {
idpId: idp.idpId,
userId: idp.userId,
userName: idp.userName,
};
const req: Partial<AddHumanUserRequest> = {
username: idp.userName,
email: {
email: idp.rawInformation?.User?.email,
isVerified: true,
},
// organisation: Organisation | undefined;
profile: {
displayName: idp.rawInformation?.User?.name ?? "",
firstName: idp.rawInformation?.User?.given_name ?? "",
lastName: idp.rawInformation?.User?.family_name ?? "",
},
idpLinks: [idpLink],
};
return req;
},
[ProviderSlug.GITHUB]: (idp: IDPInformation) => {
const idpLink: IDPLink = {
idpId: idp.idpId,
userId: idp.userId,
userName: idp.userName,
};
const req: Partial<AddHumanUserRequest> = {
username: idp.userName,
email: {
email: idp.rawInformation?.email,
isVerified: true,
},
// organisation: Organisation | undefined;
profile: {
displayName: idp.rawInformation?.name ?? "",
firstName: idp.rawInformation?.name ?? "",
lastName: idp.rawInformation?.name ?? "",
},
idpLinks: [idpLink],
};
return req;
},
};

function retrieveIDP(
id: string,
token: string
): Promise<IDPInformation | undefined> {
const userService = user.getUser(server);
return userService
.retrieveIdentityProviderInformation({ intentId: id, token: token }, {})
.then((resp: RetrieveIdentityProviderInformationResponse) => {
return resp.idpInformation;
});
}

function createUser(
provider: ProviderSlug,
info: IDPInformation
): Promise<string> {
const userData = PROVIDER_MAPPING[provider](info);
const userService = user.getUser(server);
return userService.addHumanUser(userData, {}).then((resp) => resp.userId);
}

export default async function Page({
searchParams,
params,
}: {
searchParams: Record<string | number | symbol, string | undefined>;
params: { provider: ProviderSlug };
}) {
const { id, token } = searchParams;
const { provider } = params;

if (provider && id && token) {
return retrieveIDP(id, token)
.then((information) => {
if (information) {
return createUser(provider, information).catch((error) => {
throw new Error(error.details);
});
} else {
throw new Error("Could not get user information.");
}
})
.then((userId) => {
return (
<div className="flex flex-col items-center space-y-4">
<h1>Register successful</h1>
<div>You have successfully been registered!</div>
</div>
);
})
.catch((error: Error) => {
return (
<div className="flex flex-col items-center space-y-4">
<h1>Register failed</h1>
<div className="w-full">
{
<Alert type={AlertType.ALERT}>
{JSON.stringify(error.message)}
</Alert>
}
</div>
</div>
);
});
} else {
return (
<div className="flex flex-col items-center space-y-4">
<h1>Register</h1>
<p className="ztdl-p">No id and token received!</p>
</div>
);
}
}
50 changes: 50 additions & 0 deletions apps/login/app/(login)/register/idp/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { getLegalAndSupportSettings, server } from "#/lib/zitadel";
import { SignInWithIDP } from "#/ui/SignInWithIDP";
import {
GetActiveIdentityProvidersResponse,
IdentityProvider,
ZitadelServer,
settings,
} from "@zitadel/server";

function getIdentityProviders(
server: ZitadelServer,
orgId?: string
): Promise<IdentityProvider[] | undefined> {
const settingsService = settings.getSettings(server);
return settingsService
.getActiveIdentityProviders(
orgId ? { ctx: { orgId } } : { ctx: { instance: true } },
{}
)
.then((resp: GetActiveIdentityProvidersResponse) => {
return resp.identityProviders;
});
}

export default async function Page() {
const legal = await getLegalAndSupportSettings(server);

// TODO if org idps should be shown replace emptystring with the orgId.
const identityProviders = await getIdentityProviders(server, "");

const host = process.env.VERCEL_URL
? `https://${process.env.VERCEL_URL}`
: "http://localhost:3000";

return (
<div className="flex flex-col items-center space-y-4">
<h1>Register</h1>
<p className="ztdl-p">
Select one of the following providers to register
</p>

{legal && identityProviders && process.env.ZITADEL_API_URL && (
<SignInWithIDP
host={host}
identityProviders={identityProviders}
></SignInWithIDP>
)}
</div>
);
}
19 changes: 19 additions & 0 deletions apps/login/app/api/idp/start/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { server, startIdentityProviderFlow } from "#/lib/zitadel";
import { NextRequest, NextResponse } from "next/server";

export async function POST(request: NextRequest) {
const body = await request.json();
if (body) {
let { idpId, successUrl, failureUrl } = body;

return startIdentityProviderFlow(server, { idpId, successUrl, failureUrl })
.then((resp) => {
return NextResponse.json(resp);
})
.catch((error) => {
return NextResponse.json(error, { status: 500 });
});
} else {
return NextResponse.json({}, { status: 400 });
}
}
35 changes: 19 additions & 16 deletions apps/login/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import ThemeWrapper from "#/ui/ThemeWrapper";
import { getBrandingSettings } from "#/lib/zitadel";
import { server } from "../lib/zitadel";
import { BrandingSettings } from "@zitadel/server";
import ThemeProvider from "#/ui/ThemeProvider";

const lato = Lato({
weight: ["400", "700", "900"],
Expand Down Expand Up @@ -41,29 +42,31 @@ export default async function RootLayout({
<head />
<body>
<ThemeWrapper branding={partial}>
<LayoutProviders>
<div className="h-screen overflow-y-scroll bg-background-light-600 dark:bg-background-dark-600 bg-[url('/grid-light.svg')] dark:bg-[url('/grid-dark.svg')]">
{showNav && <GlobalNav />}
<ThemeProvider>
<LayoutProviders>
<div className="h-screen overflow-y-scroll bg-background-light-600 dark:bg-background-dark-600 bg-[url('/grid-light.svg')] dark:bg-[url('/grid-dark.svg')]">
{showNav && <GlobalNav />}

<div className={`${showNav ? "lg:pl-72" : ""} pb-4`}>
<div className="mx-auto max-w-[440px] space-y-8 pt-20 lg:py-8">
{showNav && (
<div className="rounded-lg bg-vc-border-gradient dark:bg-dark-vc-border-gradient p-px shadow-lg shadow-black/5 dark:shadow-black/20">
<div className="rounded-lg bg-background-light-400 dark:bg-background-dark-500">
<AddressBar domain={domain} />
<div className={`${showNav ? "lg:pl-72" : ""} pb-4`}>
<div className="mx-auto max-w-[440px] space-y-8 pt-20 lg:py-8">
{showNav && (
<div className="rounded-lg bg-vc-border-gradient dark:bg-dark-vc-border-gradient p-px shadow-lg shadow-black/5 dark:shadow-black/20">
<div className="rounded-lg bg-background-light-400 dark:bg-background-dark-500">
<AddressBar domain={domain} />
</div>
</div>
</div>
)}
)}

<div className="rounded-lg bg-vc-border-gradient dark:bg-dark-vc-border-gradient p-px shadow-lg shadow-black/5 dark:shadow-black/20 mb-10">
<div className="rounded-lg bg-background-light-400 dark:bg-background-dark-500 px-8 py-12">
{children}
<div className="rounded-lg bg-vc-border-gradient dark:bg-dark-vc-border-gradient p-px shadow-lg shadow-black/5 dark:shadow-black/20 mb-10">
<div className="rounded-lg bg-background-light-400 dark:bg-background-dark-500 px-8 py-12">
{children}
</div>
</div>
</div>
</div>
</div>
</div>
</LayoutProviders>
</LayoutProviders>
</ThemeProvider>
</ThemeWrapper>
<Analytics />
</body>
Expand Down
21 changes: 21 additions & 0 deletions apps/login/cypress/integration/register-idp.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { stub } from "../support/mock";

const IDP_URL = "https://example.com/idp/url";

describe("register idps", () => {
beforeEach(() => {
stub("zitadel.user.v2alpha.UserService", "StartIdentityProviderFlow", {
data: {
authUrl: IDP_URL,
},
});
});

it("should redirect the user to the correct url", () => {
cy.visit("/register/idp");
cy.get('button[e2e="google"]').click();
cy.origin(IDP_URL, { args: IDP_URL }, (url) => {
cy.location("href", { timeout: 10_000 }).should("eq", url);
});
});
});
Loading

1 comment on commit b330367

@vercel
Copy link

@vercel vercel bot commented on b330367 Aug 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

typescript-login – ./

typescript-login-git-main-zitadel.vercel.app
typescript-login-zitadel.vercel.app
typescript-login.vercel.app

Please sign in to comment.