Skip to main content

Register and Login User with Password in a Custom Login UI

Flow​

Register and Login Flow

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

Request​

curl --request POST \
--url https://$ZITADEL_DOMAIN/v2beta/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": {
"givenName": "Minnie",
"familyName": "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​

{
"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:

"sendCode": {
"urlTemplate": "https://example.com/email/verify?userID={{.UserID}}&code={{.Code}}&orgID={{.OrgID}}"
},

Return Code:

"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:

Create Session with User Check​

After you have created a new user, you can redirect them 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.

Request​

curl --request POST \
--url https://$ZITADEL_DOMAIN/v2beta/sessions \
--header 'Accept: application/json' \
--header 'Authorization: Bearer '"$TOKEN"'' \
--header 'Content-Type: application/json' \
--data '{
"checks": {
"user": {
"loginName": "minnie-mouse@fabi.zitadel.app"
}
}
}'

Response​

{
"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.

{
"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"
}
}
}
}

Checkout how to handle session validation.

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​

curl --request PATCH \
--url https://$ZITADEL_DOMAIN/v2beta/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.

{
"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.

{
"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)​

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

Request​

curl --request POST \
--url https://$ZITADEL_DOMAIN/v2beta/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​

{
"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"
}
}
}
]
}

Logout User​

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

Sessions can be terminated by either:

  • the authenticated user
  • a manager, who is granted session.delete (e.g. ORG_OWNER) on the authenticated users organisation
  • providing the current session_token in the body.

Terminating a session means to delete it. If you try to read or update the session afterward, you will get an error that the Session does not exist or was terminated.

Request for authenticated users or managers​

Make sure that the provided token is from the authenticated user, resp. the manager:

curl --request DELETE \
--url https://$ZITADEL_DOMAIN/v2beta/sessions/218480890961985793 \
--header 'Accept: application/json' \
--header 'Authorization: Bearer '"$TOKEN"''\
--header 'Content-Type: application/json'

Request with session token​

Send the session token in the body of the request:

curl --request DELETE \
--url https://$ZITADEL_DOMAIN/v2beta/sessions/218480890961985793 \
--header 'Accept: application/json' \
--header 'Authorization: Bearer '"$TOKEN"''\
--header 'Content-Type: application/json' \
--data '{
"sessionToken": "blGKerGQPKv8jN21p6E9GB1B-vl6_EyKlvTd5UALu8-aQmjucgZxHSXJx3XMFTwT9_Y3VnbOo3gC_Q"
}'