mirror of
https://github.com/zitadel/zitadel.git
synced 2024-12-12 02:54:20 +00:00
Merge branch 'main' into rename-topt
This commit is contained in:
commit
1461977d93
4826
console/package-lock.json
generated
4826
console/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -12,18 +12,18 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^16.0.1",
|
||||
"@angular/cdk": "^16.0.1",
|
||||
"@angular/common": "^16.0.1",
|
||||
"@angular/compiler": "^16.0.1",
|
||||
"@angular/core": "^16.0.1",
|
||||
"@angular/forms": "^16.0.1",
|
||||
"@angular/material": "^16.0.1",
|
||||
"@angular/material-moment-adapter": "^16.0.1",
|
||||
"@angular/platform-browser": "^16.0.1",
|
||||
"@angular/platform-browser-dynamic": "^16.0.1",
|
||||
"@angular/router": "^16.0.1",
|
||||
"@angular/service-worker": "^16.0.1",
|
||||
"@angular/animations": "^16.1.2",
|
||||
"@angular/cdk": "^16.1.2",
|
||||
"@angular/common": "^16.1.2",
|
||||
"@angular/compiler": "^16.1.2",
|
||||
"@angular/core": "^16.1.2",
|
||||
"@angular/forms": "^16.1.2",
|
||||
"@angular/material": "^16.1.2",
|
||||
"@angular/material-moment-adapter": "^16.1.2",
|
||||
"@angular/platform-browser": "^16.1.2",
|
||||
"@angular/platform-browser-dynamic": "^16.1.2",
|
||||
"@angular/router": "^16.1.2",
|
||||
"@angular/service-worker": "^16.1.2",
|
||||
"@ctrl/ngx-codemirror": "^6.1.0",
|
||||
"@grpc/grpc-js": "^1.8.14",
|
||||
"@ngx-translate/core": "^14.0.0",
|
||||
@ -41,7 +41,8 @@
|
||||
"libphonenumber-js": "^1.10.30",
|
||||
"material-design-icons-iconfont": "^6.1.1",
|
||||
"moment": "^2.29.4",
|
||||
"ngx-color": "^8.0.3",
|
||||
"opentype.js": "^1.3.4",
|
||||
"ngx-color": "^9.0.0",
|
||||
"rxjs": "~7.8.0",
|
||||
"tinycolor2": "^1.6.0",
|
||||
"tslib": "^2.4.1",
|
||||
@ -49,25 +50,26 @@
|
||||
"zone.js": "~0.13.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^16.0.1",
|
||||
"@angular-devkit/build-angular": "^16.1.1",
|
||||
"@angular-eslint/builder": "16.0.1",
|
||||
"@angular-eslint/eslint-plugin": "16.0.1",
|
||||
"@angular-eslint/eslint-plugin-template": "16.0.1",
|
||||
"@angular-eslint/schematics": "16.0.1",
|
||||
"@angular-eslint/template-parser": "16.0.1",
|
||||
"@angular/cli": "^16.0.1",
|
||||
"@angular/compiler-cli": "^16.0.1",
|
||||
"@angular/language-service": "^16.0.1",
|
||||
"@angular/cli": "^16.1.1",
|
||||
"@angular/compiler-cli": "^16.1.2",
|
||||
"@angular/language-service": "^16.1.2",
|
||||
"@bufbuild/buf": "^1.18.0-1",
|
||||
"@types/file-saver": "^2.0.2",
|
||||
"@types/google-protobuf": "^3.15.3",
|
||||
"@types/jasmine": "~4.3.0",
|
||||
"@types/jasmine": "~4.3.3",
|
||||
"@types/jasminewd2": "~2.0.10",
|
||||
"@types/jsonwebtoken": "^9.0.1",
|
||||
"@types/node": "^18.15.11",
|
||||
"@types/opentype.js": "^1.3.4",
|
||||
"@types/qrcode": "^1.5.0",
|
||||
"@types/uuid": "^9.0.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.59.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.59.11",
|
||||
"@typescript-eslint/parser": "^5.59.5",
|
||||
"codelyzer": "^6.0.2",
|
||||
"eslint": "^8.39.0",
|
||||
|
@ -492,7 +492,9 @@
|
||||
</cnsl-info-section>
|
||||
<div class="font-preview" *ngIf="previewData.fontUrl; else addFontButton">
|
||||
<mat-icon class="icon">text_fields</mat-icon>
|
||||
|
||||
<span class="font-name" [ngStyle]="fontName ? { 'font-family': 'brandingFont' } : { '': '' }">{{
|
||||
fontName
|
||||
}}</span>
|
||||
<span class="fill-space"></span>
|
||||
|
||||
<button
|
||||
@ -634,6 +636,7 @@
|
||||
class="preview"
|
||||
[ngClass]="{ darkmode: theme === Theme.DARK, lightmode: theme === Theme.LIGHT }"
|
||||
[policy]="view === View.PREVIEW ? previewData : data"
|
||||
[ngStyle]="fontName ? { 'font-family': 'brandingFont' } : { '': '' }"
|
||||
>
|
||||
</cnsl-preview>
|
||||
</div>
|
||||
|
@ -243,8 +243,6 @@
|
||||
}
|
||||
|
||||
.font-preview {
|
||||
height: 70px;
|
||||
width: 70px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@ -252,22 +250,17 @@
|
||||
margin-bottom: 1rem;
|
||||
border: 1px solid map-get($foreground, divider);
|
||||
position: relative;
|
||||
padding: 1rem 0.5rem 1rem 0.75rem;
|
||||
|
||||
.icon {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%) translateY(-50%);
|
||||
.font-name {
|
||||
margin-left: 0.5rem;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.dl-btn {
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
cursor: pointer;
|
||||
visibility: hidden;
|
||||
transform: translateX(50%) translateY(-50%);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
|
@ -31,6 +31,7 @@ import {
|
||||
} from 'src/app/services/theme.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
import * as opentype from 'opentype.js';
|
||||
import { InfoSectionType } from '../../info-section/info-section.component';
|
||||
import { WarnDialogComponent } from '../../warn-dialog/warn-dialog.component';
|
||||
import { PolicyComponentServiceType } from '../policy-component-types.enum';
|
||||
@ -88,6 +89,8 @@ export class PrivateLabelingPolicyComponent implements OnInit, OnDestroy {
|
||||
public ColorType: any = ColorType;
|
||||
public AssetType: any = AssetType;
|
||||
|
||||
public fontName = '';
|
||||
|
||||
public refreshPreview: EventEmitter<void> = new EventEmitter();
|
||||
public org!: Org.AsObject;
|
||||
public InfoSectionType: any = InfoSectionType;
|
||||
@ -180,6 +183,10 @@ export class PrivateLabelingPolicyComponent implements OnInit, OnDestroy {
|
||||
if (file) {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
|
||||
this.getFontName(file);
|
||||
this.previewNewFont(file);
|
||||
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
return this.handleFontUploadPromise(this.assetService.upload(AssetEndpoint.MGMTFONT, formData, this.org.id));
|
||||
@ -199,6 +206,7 @@ export class PrivateLabelingPolicyComponent implements OnInit, OnDestroy {
|
||||
this.getPreviewData().then((data) => {
|
||||
if (data.policy) {
|
||||
this.previewData = data.policy;
|
||||
this.fontName = '';
|
||||
}
|
||||
});
|
||||
}, 1000);
|
||||
@ -374,6 +382,11 @@ export class PrivateLabelingPolicyComponent implements OnInit, OnDestroy {
|
||||
.then((data) => {
|
||||
if (data.policy) {
|
||||
this.previewData = data.policy;
|
||||
if (this.previewData?.fontUrl) {
|
||||
this.fetchFontMetadataAndPreview(this.previewData.fontUrl);
|
||||
} else {
|
||||
this.fontName = 'Could not parse font name';
|
||||
}
|
||||
this.loading = false;
|
||||
}
|
||||
})
|
||||
@ -385,6 +398,11 @@ export class PrivateLabelingPolicyComponent implements OnInit, OnDestroy {
|
||||
.then((data) => {
|
||||
if (data.policy) {
|
||||
this.data = data.policy;
|
||||
if (this.data?.fontUrl) {
|
||||
this.fetchFontMetadataAndPreview(this.data?.fontUrl);
|
||||
} else {
|
||||
this.fontName = 'Could not parse font name';
|
||||
}
|
||||
this.loading = false;
|
||||
}
|
||||
})
|
||||
@ -678,6 +696,45 @@ export class PrivateLabelingPolicyComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
private fetchFontMetadataAndPreview(url: string): void {
|
||||
fetch(url)
|
||||
.then((res) => res.blob())
|
||||
.then((blob) => {
|
||||
this.getFontName(blob);
|
||||
this.previewNewFont(blob);
|
||||
});
|
||||
}
|
||||
|
||||
private getFontName(blob: Blob): void {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
if (e.target && e.target.result) {
|
||||
try {
|
||||
const font = opentype.parse(e.target.result);
|
||||
this.fontName = font.names.fullName['en'];
|
||||
} catch (e) {
|
||||
this.fontName = 'Could not parse font name';
|
||||
}
|
||||
}
|
||||
};
|
||||
reader.readAsArrayBuffer(blob);
|
||||
}
|
||||
|
||||
private previewNewFont(blob: Blob): void {
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onload = (e) => {
|
||||
if (e.target) {
|
||||
let customFont = new FontFace('brandingFont', `url(${e.target.result})`);
|
||||
// typescript complains that add is not found but
|
||||
// indeed it is https://developer.mozilla.org/en-US/docs/Web/API/FontFaceSet/add
|
||||
// @ts-ignore
|
||||
document.fonts.add(customFont);
|
||||
}
|
||||
};
|
||||
reader.readAsDataURL(blob);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * defaults to false because urls are distinct anyway
|
||||
// */
|
||||
|
20
docs/docs/guides/integrate/login-ui/_logout.mdx
Normal file
20
docs/docs/guides/integrate/login-ui/_logout.mdx
Normal file
@ -0,0 +1,20 @@
|
||||
When your user is done using your application and clicks on the logout button, you have to send a request to the terminate session endpoint.
|
||||
[Terminate Session Documentation](https://zitadel.com/docs/apis/resources/session_service/session-service-delete-session)
|
||||
|
||||
Send the session token in the body of the request.
|
||||
|
||||
### Request
|
||||
|
||||
```bash
|
||||
curl --request DELETE \
|
||||
--url https://$ZITADEL_DOMAIN/v2alpha/sessions/218480890961985793 \
|
||||
--header 'Accept: application/json' \
|
||||
--header 'Authorization: Bearer '"$TOKEN"''\
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"sessionToken": "blGKerGQPKv8jN21p6E9GB1B-vl6_EyKlvTd5UALu8-aQmjucgZxHSXJx3XMFTwT9_Y3VnbOo3gC_Q"
|
||||
}'
|
||||
```
|
||||
|
||||
Terminating a session means to delete it.
|
||||
If you try to read the session afterwards, you will get an error “Session does not exist”.
|
80
docs/docs/guides/integrate/login-ui/_select-account.mdx
Normal file
80
docs/docs/guides/integrate/login-ui/_select-account.mdx
Normal file
@ -0,0 +1,80 @@
|
||||
|
||||
If you want to build your own select account/account picker, you have to cache the session IDs.
|
||||
We recommend storing a list of the session Ids with the corresponding session token in a cookie.
|
||||
The list of session IDs can be sent in the “search sessions” request to get a detailed list of sessions for the account selection.
|
||||
|
||||
[Search Sessions Documentation](https://zitadel.com/docs/apis/resources/session_service/session-service-list-sessions)
|
||||
|
||||
### Request
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url https://$ZITADEL_DOMAIN/v2alpha/sessions/_search \
|
||||
--header 'Accept: application/json' \
|
||||
--header 'Authorization: Bearer '"$TOKEN"''\
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"query": {
|
||||
"offset": "0",
|
||||
"limit": 100,
|
||||
"asc": true
|
||||
},
|
||||
"queries": [
|
||||
{
|
||||
"idsQuery": {
|
||||
"ids": [
|
||||
"218380657934467329", "218480890961985793"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
### Response
|
||||
|
||||
```bash
|
||||
{
|
||||
"details": {
|
||||
"totalResult": "2",
|
||||
"processedSequence": "582",
|
||||
"timestamp": "2023-06-14T05:42:11.644220Z"
|
||||
},
|
||||
"sessions": [
|
||||
{
|
||||
"id": "218380657934467329",
|
||||
"creationDate": "2023-06-13T12:56:56.683629Z",
|
||||
"changeDate": "2023-06-13T12:56:56.724450Z",
|
||||
"sequence": "574",
|
||||
"factors": {
|
||||
"user": {
|
||||
"verifiedAt": "2023-06-13T12:56:55.440850Z",
|
||||
"id": "218380659823356328",
|
||||
"loginName": "minnie-mouse@fabi.zitadel.app",
|
||||
"displayName": "Minnie Mouse"
|
||||
},
|
||||
"password": {
|
||||
"verifiedAt": "2023-06-13T12:56:56.675359Z"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "218480890961985793",
|
||||
"creationDate": "2023-06-14T05:32:38.977954Z",
|
||||
"changeDate": "2023-06-14T05:42:11.631901Z",
|
||||
"sequence": "582",
|
||||
"factors": {
|
||||
"user": {
|
||||
"verifiedAt": "2023-06-14T05:32:38.972712Z",
|
||||
"id": "218380659823356328",
|
||||
"loginName": "minnie-mouse@fabi.zitadel.app",
|
||||
"displayName": "Minnie Mouse"
|
||||
},
|
||||
"password": {
|
||||
"verifiedAt": "2023-06-14T05:42:11.619484Z"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
209
docs/docs/guides/integrate/login-ui/external-login.mdx
Normal file
209
docs/docs/guides/integrate/login-ui/external-login.mdx
Normal file
@ -0,0 +1,209 @@
|
||||
---
|
||||
title: Handle External Login
|
||||
sidebar_label: External Identity Provider
|
||||
---
|
||||
|
||||
## Flow
|
||||
|
||||
The prerequisite for adding an external login (social and enterprise) to your user account is a registered identity provider on your ZITADEL instance or the organization of the user.
|
||||
If you haven’t added a provider yet, have a look at the following guide first: [Identity Providers](https://zitadel.com/docs/guides/integrate/identity-providers)
|
||||
|
||||
![Identity Provider Flow](/img/guides/login-ui/external-login-flow.png)
|
||||
|
||||
## Start the Provider Flow
|
||||
|
||||
ZITADEL will handle as much as possible from the authentication flow with the external provider.
|
||||
This requires you to initiate the flow with your desired provider.
|
||||
|
||||
Send the following two URLs in the request body:
|
||||
1. SuccessURL: Page that should be shown when the login was successful
|
||||
2. ErrorURL: Page that should be shown when an error happens during the authentication
|
||||
|
||||
In the response, you will get an authentication URL of the provider you like.
|
||||
[Start Identity Provider Flow Documentation](https://zitadel.com/docs/apis/resources/user_service/user-service-start-identity-provider-flow)
|
||||
|
||||
### Request
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url https://$ZITADEL_DOMAIN/v2alpha/users/idps/$IDP_ID/start \
|
||||
--header 'Accept: application/json' \
|
||||
--header 'Authorization: Bearer '"$TOKEN"''\
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"successUrl": "https://custom.com/login/idp/success",
|
||||
"failureUrl": "https://custom.com/login/idp/fail"
|
||||
}'
|
||||
```
|
||||
|
||||
### Response
|
||||
|
||||
```bash
|
||||
{
|
||||
"details": {
|
||||
"sequence": "592",
|
||||
"changeDate": "2023-06-14T12:51:29.654819Z",
|
||||
"resourceOwner": "163840776835432705"
|
||||
},
|
||||
"authUrl": "https://accounts.google.com/o/oauth2/v2/auth?client_id=Test&prompt=select_account&redirect_uri=https%3A%2F%2F$ZITADEL_DOMAIN%2Fidps%2Fcallback&response_type=code&scope=openid+profile+email&state=218525066445455617"
|
||||
}
|
||||
```
|
||||
|
||||
## Call Provider
|
||||
|
||||
The next step is to call the auth URL you got in the response from the previous step.
|
||||
This will open up the login page of the given provider. In this guide, it is Google Login.
|
||||
|
||||
```bash
|
||||
https://accounts.google.com/o/oauth2/v2/auth?client_id=Test&prompt=select_account&redirect_uri=https%3A%2F%2F$ZITADEL_DOMAIN%2Fidps%2Fcallback&response_type=code&scope=openid+profile+email&state=218525066445455617
|
||||
```
|
||||
|
||||
After the user has successfully authenticated, a redirect to the ZITADEL backend /idps/callback will automatically be performed.
|
||||
|
||||
## Get Provider Information
|
||||
|
||||
ZITADEL will take the information of the provider. After this, a redirect will be made to either the success page in case of a successful login or to the error page in case of a failure will be performed. In the parameters, you will provide the intentID, a token, and optionally, if a user could be found, a user ID.
|
||||
|
||||
To get the information of the provider, make a request to ZITADEL.
|
||||
[Get Identity Provider Information Documentation](https://zitadel.com/docs/apis/resources/user_service/user-service-retrieve-identity-provider-information)
|
||||
|
||||
### Request
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url https://$ZITADEL_DOMAIN/v2alpha/users/intents/$INTENT_ID/information \
|
||||
--header 'Accept: application/json' \
|
||||
--header 'Authorization: Bearer '"$TOKEN"''\
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"token": "k50WQmDaPIazQDJsyKaEPaQPwgsytxqgQ3K1ifQeQtAmeQ"
|
||||
}'
|
||||
```
|
||||
|
||||
### Response
|
||||
|
||||
```bash
|
||||
{
|
||||
"details": {
|
||||
"sequence": "599",
|
||||
"changeDate": "2023-06-15T06:44:26.039444Z",
|
||||
"resourceOwner": "163840776835432705"
|
||||
},
|
||||
"idpInformation": {
|
||||
"oauth": {
|
||||
"accessToken": "ya29.a0AWY7CknrOORopcJK2XX2fQXV9NQpp8JdkKYx-mQZNrR-wktWWhc3QsepT6kloSCgFPS9N0beEBlEYoY5oYUhfc7VlLHTQz5iECE386pyx5YmTueyeQ9GXk1dAw89gi8KRyjNlJApFsfLJaoiLIvKJLf23eAyXgaCgYKAUMSARESFQG1tDrpnTJ2su8BBO24zfmzgneARw0165",
|
||||
"idToken": "eyJhbGciOiJSUzI1NiIsImtpZCI6Ijg1YmE5MzEzZmQ3YTdkNGFmYTg0ODg0YWJjYzg0MDMwMDQzNjMxODAiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiIxODI5MDIwMjY1MDgtdW1taXQ3dHZjbHBnM2NxZmM4b2ljdGE1czI1aGtudWwuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJhdWQiOiIxODI5MDIwMjY1MDgtdW1taXQ3dHZjbHBnM2NxZmM4b2ljdGE1czI1aGtudWwuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMTEzOTI4MDU5NzU3MTU4NTY2MzciLCJoZCI6InJvb3RkLmNoIiwiZW1haWwiOiJmYWJpZW5uZUByb290ZC5jaCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJhdF9oYXNoIjoidGN5X25JTkZHNnFhRTBZTWFsQzZGdyIsIm5hbWUiOiJGYWJpZW5uZSBCw7xobGVyIiwicGljdHVyZSI6Imh0dHBzOi8vbGgzLmdvb2dsZXVzZXJjb250ZW50LmNvbS9hL0FBY0hUdGY5NzNRNk5IOEt6S1RNRVpFTFBVOWx4NDVXcFE5RlJCdXhGZFBiPXM5Ni1jIiwiZ2l2ZW5fbmFtZSI6IkZhYmllbm5lIiwiZmFtaWx5X25hbWUiOiJCw7xobGVyIiwibG9jYWxlIjoiZGUiLCJpYXQiOjE2ODY4MTE0NjUsImV4cCI6MTY4NjgxNTA2NX0.PwlAHRM44e8eYyHzdfotOrcq5GZc4D15mWvN3rGdoDmu2RRgb4T0nDgkY6AVq2vNJxPfbiB1jFtNP6dgX-OgLIxNXg_tJJhwFh-eFPA37cIiv1CEcgEC-q1zXFIa3HrwHLreeU6i7C9JkDrKpkS-AKat1krf27QXxrxHLrWehi5F2l1OZuAKFWYaYmJOd0sVTDBA2o5SDcAiQs_D4-Q-kSl5f0gh607YVHLv7zjyfHtAOs7xPEkNEUVcqGBke2Zy9kAYIgiMriNxLA2EDxFtSyWnf-bCXKnuVX2hwEY0T0lUPrOAVkz7MEOKiacE2xAOczoQvl-wECU0UofLi8XZqg"
|
||||
},
|
||||
"idpId": "218528353504723201",
|
||||
"rawInformation": {
|
||||
"User": {
|
||||
"email": "fabienne@rootd.ch",
|
||||
"email_verified": true,
|
||||
"family_name": "Bühler",
|
||||
"given_name": "Fabienne",
|
||||
"hd": "rootd.ch",
|
||||
"locale": "de",
|
||||
"name": "Fabienne Bühler",
|
||||
"picture": "https://lh3.googleusercontent.com/a/AAcKTtf973Q6NH8KzKTMEZELPU9lx45WpQ9FRBuxFdPb=s96-c",
|
||||
"sub": "111392805975715856637"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Handle Provider Information
|
||||
After successfully authenticating using your identity provider, you have three possible options.
|
||||
1. Login
|
||||
2. Register user
|
||||
3. Add social login to existing user
|
||||
|
||||
### Login
|
||||
|
||||
If you did get a user ID in the parameters when calling your success page, you know that a user is already linked with the used identity provider and you are ready to perform the login.
|
||||
Create a new session and include the intent ID and the token in the checks.
|
||||
This check requires that the previous step ended on the successful page and didn't’t result in an error.
|
||||
|
||||
#### Request
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url https://$ZITADEL_DOMAIN/v2alpha/sessions \
|
||||
--header 'Accept: application/json' \
|
||||
--header 'Authorization: Bearer '"$TOKEN"''\
|
||||
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"checks": {
|
||||
"user": {
|
||||
"userId": "218662596918640897"
|
||||
},
|
||||
"intent": {
|
||||
"intentId": "219647325729980673",
|
||||
"token": "k86ihn-VLMMUGKy1q1b5i_foECspKYqei1l4mS8LT7Xzjw"
|
||||
}
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
### Register
|
||||
|
||||
If you didn't get a user ID in the parameters of your success page, you know that there is no existing user in ZITADEL with that provider, and you can register a new user or link it to an existing account (read the next section).
|
||||
|
||||
Fill the IdP links in the create user request to add a user with an external login provider.
|
||||
The idpId is the ID of the provider in ZITADEL, the idpExternalId is the ID of the user in the external identity provider; usually, this is sent in the “sub”.
|
||||
The display name is used to list the linkings on the users.
|
||||
|
||||
[Create User API Documentation](https://zitadel.com/docs/apis/resources/user_service/user-service-add-human-user)
|
||||
|
||||
#### Request
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url https://$ZITADEL_DOMAIN/v2alpha/users/human \
|
||||
--header 'Accept: application/json' \
|
||||
--header 'Authorization: Bearer '"$TOKEN"''\
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"username": "minni-mouse@mouse.com",
|
||||
"profile": {
|
||||
"firstName": "Minnie",
|
||||
"lastName": "Mouse",
|
||||
"nickName": "Mini",
|
||||
"displayName": "Minnie Mouse",
|
||||
"preferredLanguage": "en",
|
||||
"gender": "GENDER_FEMALE"
|
||||
},
|
||||
"email": {
|
||||
"email": "minni-mouse@mouse.com",
|
||||
"isVerified": true
|
||||
},
|
||||
"idpLinks": [
|
||||
{
|
||||
"idpId": "218528353504723201",
|
||||
"idpExternalId": "111392805975715856637",
|
||||
"displayName": "Minnie Mouse"
|
||||
}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
### Add External Login to Existing User
|
||||
|
||||
If you didn't get a user ID in the parameters to your success page, you know that there is no existing user in ZITADEL with that provider and you can register a new user (read previous section), or link it to an existing account.
|
||||
|
||||
If you want to link/connect to an existing account you can perform the add identity provider link request.
|
||||
[Add IDP Link to existing user documentation](https://zitadel.com/docs/apis/resources/user_service/user-service-add-idp-link)
|
||||
|
||||
#### Request
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url https://$ZITADEL_DOMAIN/v2alpha/users/users/218385419895570689/links \
|
||||
--header 'Accept: application/json' \
|
||||
--header 'Authorization: Bearer '"$TOKEN"''\
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"idpLink": {
|
||||
"idpId": "218528353504723201",
|
||||
"idpExternalId": "1113928059757158566371",
|
||||
"displayName": "Minnie Mouse"
|
||||
}
|
||||
}'
|
||||
```
|
7
docs/docs/guides/integrate/login-ui/logout.mdx
Normal file
7
docs/docs/guides/integrate/login-ui/logout.mdx
Normal file
@ -0,0 +1,7 @@
|
||||
---
|
||||
title: Logout
|
||||
---
|
||||
|
||||
import Logout from './_logout.mdx';
|
||||
|
||||
<Logout/>
|
26
docs/docs/guides/integrate/login-ui/oidc-standard.mdx
Normal file
26
docs/docs/guides/integrate/login-ui/oidc-standard.mdx
Normal file
@ -0,0 +1,26 @@
|
||||
---
|
||||
title: OIDC Standard
|
||||
---
|
||||
|
||||
:::info
|
||||
Not yet implemented, but should give you a general impression of how it will work
|
||||
Subscribe to the following issue: https://github.com/orgs/zitadel/projects/2/views/1?filterQuery=oidc&pane=issue&itemId=23181369
|
||||
:::
|
||||
|
||||
To build your own login ui for your own application it is not necessary to have the OIDC standard included or any additional work that has to be done.
|
||||
However, it might make sense, if you want to connect your login to different applications especially if they are not in your control and they rely on the standard.
|
||||
|
||||
The following flow shows you the different components you need to enable OIDC for your login.
|
||||
![OIDC Flow](/img/guides/login-ui/oidc-flow.png)
|
||||
|
||||
1. Your application makes an authorization request to your login UI
|
||||
2. The login UI takes the requests and sends them to the ZITADEL API. In the request to the ZITADEL API, a header to authenticate your client is needed.
|
||||
3. The ZITADEL API parses the request and does what it needs to interpret certain parameters (e.g., organization scope, etc.)
|
||||
4. Redirect to a predefined, relative URL of the login UI that includes the authrequest ID
|
||||
5. Request to ZITADEL API to get all the information from the auth request
|
||||
6. Create and update the session till the login flow is complete and the user is authenticated. Make sure to include the auth Request ID in the session
|
||||
7. Read the callback URL from the ZITADEL API
|
||||
8. Redirect to your application with the callback URL you got in the previous request
|
||||
9. All OIDC-specific endpoints have to be accepted in the Login UI and should be proxied and sent to the ZITADEL API
|
||||
|
||||
|
7
docs/docs/guides/integrate/login-ui/select-account.mdx
Normal file
7
docs/docs/guides/integrate/login-ui/select-account.mdx
Normal file
@ -0,0 +1,7 @@
|
||||
---
|
||||
title: Select Account
|
||||
---
|
||||
|
||||
import SelectAccount from './_select-account.mdx';
|
||||
|
||||
<SelectAccount/>
|
236
docs/docs/guides/integrate/login-ui/username-password.mdx
Normal file
236
docs/docs/guides/integrate/login-ui/username-password.mdx
Normal file
@ -0,0 +1,236 @@
|
||||
---
|
||||
title: Register and Login User with Password
|
||||
sidebar_label: Username and Password
|
||||
---
|
||||
|
||||
## Flow
|
||||
|
||||
![Register and Login Flow](/img/guides/login-ui/username-password-flow.png)
|
||||
|
||||
## Register
|
||||
|
||||
First, we create a new user with a username and password. In the example below we add a user with profile data, a verified email address, and a password.
|
||||
[Create User Documentation](https://zitadel.com/docs/apis/resources/user_service/user-service-add-human-user)
|
||||
|
||||
### Request
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url https://$ZITADEL_DOMAIN/v2alpha/users/human \
|
||||
--header 'Accept: application/json' \
|
||||
--header 'Authorization: Bearer '"$TOKEN"'' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"userId": "d654e6ba-70a3-48ef-a95d-37c8d8a7901a",
|
||||
"username": "minnie-mouse",
|
||||
"profile": {
|
||||
"firstName": "Minnie",
|
||||
"lastName": "Mouse",
|
||||
"nickName": "Mini",
|
||||
"displayName": "Minnie Mouse",
|
||||
"preferredLanguage": "en",
|
||||
"gender": "GENDER_FEMALE"
|
||||
},
|
||||
"email": {
|
||||
"email": "mini@mouse.com",
|
||||
"isVerified": true
|
||||
},
|
||||
"metadata": [
|
||||
{
|
||||
"key": "my-key",
|
||||
"value": "VGhpcyBpcyBteSB0ZXN0IHZhbHVl"
|
||||
}
|
||||
],
|
||||
"password": {
|
||||
"password": "Secr3tP4ssw0rd!",
|
||||
"changeRequired": false
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
### Response
|
||||
|
||||
```bash
|
||||
{
|
||||
"userId": "d654e6ba-70a3-48ef-a95d-37c8d8a7901a",
|
||||
"details": {
|
||||
"sequence": "570",
|
||||
"changeDate": "2023-06-13T12:44:36.967851Z",
|
||||
"resourceOwner": "163840776835432705"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If you want the user to verify the email address you can either choose that ZITADEL sends a verification email to the user by adding a urlTemplate into the sendCode, or ask for a return code to send your own emails.
|
||||
|
||||
Send Email by ZITADEL:
|
||||
|
||||
```bash
|
||||
"sendCode": {
|
||||
"urlTemplate": "https://example.com/email/verify?userID={{.UserID}}&code={{.Code}}&orgID={{.OrgID}}"
|
||||
},
|
||||
```
|
||||
|
||||
Return Code:
|
||||
```bash
|
||||
"email": {
|
||||
"email": "mini@mouse.com",
|
||||
"returnCode": {}
|
||||
},
|
||||
```
|
||||
|
||||
To check what is allowed on your instance, call the settings service for more information.
|
||||
The following requests can be useful for registration:
|
||||
- [Get Login Settings](https://zitadel.com/docs/apis/resources/settings_service/settings-service-get-login-settings) To find out which authentication possibilities are enabled (password, identity provider, etc.)
|
||||
- [Get Password Complexity Settings](https://zitadel.com/docs/apis/resources/settings_service/settings-service-get-password-complexity-settings) to find out how the password should look like (length, characters, etc.)
|
||||
|
||||
## Create Session with User Check
|
||||
|
||||
After you have created a new user, you can redirect him to your login screen.
|
||||
You can either create a new empty session as soon as the first login screen is shown or update it with each piece of information you get throughout the process.
|
||||
Or you can create a new session with the first credentials a user enters.
|
||||
In the following example, we assume that the login flow asks for the username in the first step, and in the second for the password.
|
||||
In API requests, this means creating a new session with a username and updating it with the password.
|
||||
If you already have the userId from a previous register, you can send it directly to skip the username and show the password screen directly.
|
||||
The create and update session endpoints will always return a session ID and an opaque session token.
|
||||
If you do not rely on the OIDC standard you can directly use the token.
|
||||
Send it to the Get Session Endpoint to find out how the user has authenticated.
|
||||
|
||||
- [Create new session Documentation](https://zitadel.com/docs/apis/resources/session_service/session-service-create-session)
|
||||
- [Update an existing session Documentation](https://zitadel.com/docs/apis/resources/session_service/session-service-set-session)
|
||||
- [Get Session Documentation](https://zitadel.com/docs/apis/resources/session_service/session-service-get-session)
|
||||
|
||||
### Request
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url https://$ZITADEL_DOMAIN/v2alpha/sessions \
|
||||
--header 'Accept: application/json' \
|
||||
--header 'Authorization: Bearer '"$TOKEN"'' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"checks": {
|
||||
"user": {
|
||||
"loginName": "minnie-mouse@fabi.zitadel.app"
|
||||
}
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
### Response
|
||||
|
||||
```bash
|
||||
{
|
||||
"details": {
|
||||
"sequence": "580",
|
||||
"changeDate": "2023-06-14T05:32:39.007096Z",
|
||||
"resourceOwner": "163840776835432705"
|
||||
},
|
||||
"sessionId": "218480890961985793",
|
||||
"sessionToken": "yMDi6uVPJAcphbbz0LaxC07ihWkNTe7m0Xqch8SzfM5Cz3HSIQIDZ65x1f5Qal0jxz0MEyo-_zYcUg"
|
||||
}
|
||||
```
|
||||
|
||||
### Session State
|
||||
|
||||
If you read the newly created session, it will look like the following.
|
||||
You can see the creation and change date.
|
||||
In the factors, you will see all the checks that have been made.
|
||||
In this case, the user has been checked.
|
||||
|
||||
```bash
|
||||
{
|
||||
"session": {
|
||||
"id": "218480890961985793",
|
||||
"creationDate": "2023-06-14T05:32:38.977954Z",
|
||||
"changeDate": "2023-06-14T05:32:39.007096Z",
|
||||
"sequence": "580",
|
||||
"factors": {
|
||||
"user": {
|
||||
"verifiedAt": "2023-06-14T05:32:38.972712Z",
|
||||
"id": "d654e6ba-70a3-48ef-a95d-37c8d8a7901a",
|
||||
"loginName": "minnie-mouse@fabi.zitadel.app",
|
||||
"displayName": "Minnie Mouse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Update Session with Password
|
||||
|
||||
Your session already has a username check.
|
||||
The next step is to check the password.
|
||||
To update an existing session, add the session ID to the URL and the session token you got in the previous step to the request body.
|
||||
|
||||
### Request
|
||||
|
||||
```bash
|
||||
curl --request PATCH \
|
||||
--url https://$ZITADEL_DOMAIN/v2alpha/sessions/$SESSION_ID \
|
||||
--header 'Accept: application/json' \
|
||||
--header 'Authorization: Bearer '"$TOKEN"''\
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"sessionToken": "yMDi6uVPJAcphbbz0LaxC07ihWkNTe7m0Xqch8SzfM5Cz3HSIQIDZ65x1f5Qal0jxz0MEyo-_zYcUg",
|
||||
"checks": {
|
||||
"password": {
|
||||
"password": "Secr3tP4ssw0rd!"
|
||||
}
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
### Response
|
||||
|
||||
The response of the create and update session token looks the same.
|
||||
Make sure to always use the session token of the last response you got, as the values may be updated.
|
||||
|
||||
```bash
|
||||
{
|
||||
"details": {
|
||||
"sequence": "582",
|
||||
"changeDate": "2023-06-14T05:42:11.631901Z",
|
||||
"resourceOwner": "163840776835432705"
|
||||
},
|
||||
"sessionToken": "blGKerGQPKv8jN21p6E9GB1B-vl6_EyKlvTd5UALu8-aQmjucgZxHSXJx3XMFTwT9_Y3VnbOo3gC_Q"
|
||||
}
|
||||
```
|
||||
|
||||
### Session State
|
||||
If you read your session after the password check, you will see that the check has been added to the factors with the verification date.
|
||||
|
||||
```bash
|
||||
{
|
||||
"session": {
|
||||
"id": "218480890961985793",
|
||||
"creationDate": "2023-06-14T05:32:38.977954Z",
|
||||
"changeDate": "2023-06-14T05:42:11.631901Z",
|
||||
"sequence": "582",
|
||||
"factors": {
|
||||
"user": {
|
||||
"verifiedAt": "2023-06-14T05:32:38.972712Z",
|
||||
"id": "d654e6ba-70a3-48ef-a95d-37c8d8a7901a",
|
||||
"loginName": "minnie-mouse@fabi.zitadel.app",
|
||||
"displayName": "Minnie Mouse"
|
||||
},
|
||||
"password": {
|
||||
"verifiedAt": "2023-06-14T05:42:11.619484Z"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## List the Sessions (Account Chooser)
|
||||
|
||||
import SelectAccount from './_select-account.mdx';
|
||||
|
||||
<SelectAccount/>
|
||||
|
||||
## Logout User
|
||||
|
||||
import Logout from './_logout.mdx';
|
||||
|
||||
<Logout/>
|
@ -167,6 +167,26 @@ module.exports = {
|
||||
"guides/integrate/pat",
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Build your own Login-UI",
|
||||
link: {
|
||||
type: "generated-index",
|
||||
title: "Build your own Login-UI",
|
||||
slug: "/guides/integrate/login-ui",
|
||||
description:
|
||||
"In the following guides you will learn how to create your own login ui with our APIs. The different scenarios like username/password, external identity provider, etc will be shown.",
|
||||
|
||||
},
|
||||
collapsed: true,
|
||||
items: [
|
||||
"guides/integrate/login-ui/username-password",
|
||||
"guides/integrate/login-ui/external-login",
|
||||
"guides/integrate/login-ui/select-account",
|
||||
"guides/integrate/login-ui/logout",
|
||||
"guides/integrate/login-ui/oidc-standard"
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Configure identity providers",
|
||||
|
BIN
docs/static/img/guides/login-ui/external-login-flow.png
vendored
Normal file
BIN
docs/static/img/guides/login-ui/external-login-flow.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 280 KiB |
BIN
docs/static/img/guides/login-ui/oidc-flow.png
vendored
Normal file
BIN
docs/static/img/guides/login-ui/oidc-flow.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 254 KiB |
BIN
docs/static/img/guides/login-ui/username-password-flow.png
vendored
Normal file
BIN
docs/static/img/guides/login-ui/username-password-flow.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 270 KiB |
Loading…
Reference in New Issue
Block a user