Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IDP register #35

Merged
merged 35 commits into from
Aug 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
e659824
signin component
peintnermax Jul 17, 2023
70d04c8
zitadel next components, react
peintnermax Jul 18, 2023
d3911c3
idp components
peintnermax Jul 26, 2023
f7e0d83
depends on
peintnermax Jul 26, 2023
4116f99
rm react dep in next
peintnermax Jul 26, 2023
0471307
google forwardref button
peintnermax Jul 27, 2023
f69d922
fix async await client, react components
peintnermax Jul 27, 2023
c17a2ba
fix theme provider
peintnermax Jul 28, 2023
83000f1
github mapping, signup
peintnermax Jul 28, 2023
9713e2d
fix build, azure, gitlab styling
peintnermax Jul 31, 2023
4040e20
rm unused exports / files
peintnermax Jul 31, 2023
8a8afa2
tsup
peintnermax Aug 2, 2023
4164fc0
tsup, fix build
peintnermax Aug 2, 2023
983cbeb
host
peintnermax Aug 2, 2023
f00e582
host from server page
peintnermax Aug 2, 2023
0d29d5d
VERCEL_URL globalEnv listing
peintnermax Aug 2, 2023
4cb618f
cleanup
peintnermax Aug 2, 2023
3b7e150
cleanup, show error
peintnermax Aug 2, 2023
a1f7056
acceptance tests
peintnermax Aug 2, 2023
9bbaa91
--passWithNoTests
peintnermax Aug 2, 2023
10771b7
test
peintnermax Aug 2, 2023
8e74d29
node 18
peintnermax Aug 2, 2023
37e2aec
v3 setup-node
peintnermax Aug 2, 2023
5cbb37f
actions/checkout v3
peintnermax Aug 2, 2023
d0445d6
global mock
peintnermax Aug 2, 2023
707ffae
rm unused test
peintnermax Aug 2, 2023
0a98bf1
fix test
peintnermax Aug 2, 2023
24e57e9
match href not pathname
peintnermax Aug 2, 2023
096f696
cleanup
peintnermax Aug 2, 2023
6f1da97
switch origin
eliobischof Aug 2, 2023
b5a5212
switch origin
eliobischof Aug 2, 2023
284859c
lint, unit tests
peintnermax Aug 2, 2023
dcd6822
define args for cy.origin
peintnermax Aug 2, 2023
25fbffc
remove any
peintnermax Aug 3, 2023
f1aeafc
rm release.yml
peintnermax Aug 3, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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) => {
eliobischof marked this conversation as resolved.
Show resolved Hide resolved
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