docs(guide): OneLogin SAML IdP integration guide (#11257)

Resolves #7280
This commit is contained in:
Matías Racedo
2026-01-02 09:25:01 -03:00
committed by GitHub
parent ee6d66d429
commit 9a29eebd72
6 changed files with 331 additions and 0 deletions
@@ -0,0 +1,330 @@
---
title: Configure OneLogin as a SAML Identity Provider in ZITADEL
sidebar_label: OneLogin SAML
id: onelogin-saml
---
import GeneralConfigDescription from './_general_config_description.mdx';
import Intro from './_intro.mdx';
import CustomLoginPolicy from './_custom_login_policy.mdx';
import IDPsOverview from './_idps_overview.mdx';
import Activate from './_activate.mdx';
import TestSetup from './_test_setup.mdx';
<Intro provider="OneLogin"/>
## OneLogin SAML configuration
You need access to a OneLogin admin portal with permission to create and configure SAML applications.
We’ll first configure OneLogin sufficiently to **export IdP metadata**, which we then import into ZITADEL.
After the ZITADEL SAML provider exists, we’ll return to OneLogin and finish the SAML configuration using the ZITADEL URLs.
### Create a new SAML application for ZITADEL
1. Sign in to the **OneLogin admin portal**.
2. Go to **Applications > Applications** (or **Apps > Add Apps**, depending on your UI).
3. Click **Add App**.
4. In **Find Applications**, search for:
- **SAML Test Connector (IdP w/ attr)** (recommended), or
- **SAML Custom Connector (Advanced)** if you prefer a more generic connector.
5. Select the connector and set:
- **Display Name** – for example: `ZITADEL SAML`.
- Any optional icon or description you like.
6. Click **Save** to create the application.
![Image: OneLogin Add Apps screen showing SAML connector selection](/img/guides/onelogin-add-apps.png)
### Export OneLogin IdP metadata
Now export OneLogin IdP metadata so ZITADEL can trust OneLogin as an identity provider.
1. In the OneLogin admin portal, open the **ZITADEL SAML** application.
2. To export the full metadata XML:
- In the top-right corner, open the **More Actions** menu.
- Select **SAML Metadata** to download the metadata XML file.
3. Alternatively, you can copy the **Issuer URL**, open it in a browser, and save the XML response.
This URL often serves as an IdP metadata endpoint.
Keep either:
- the **IdP Metadata URL** (Issuer URL that returns XML), or
- the **IdP metadata XML file**,
ready for the ZITADEL configuration steps.
## ZITADEL configuration
### Go to the IdP providers overview
<IDPsOverview templates="SAML"/>
### Create a new SAML Service Provider
The SAML provider template has everything you need preconfigured.
Add the metadata XML or the URL to the metadata which is accessible by your ZITADEL instance.
All the necessary configuration is contained in the metadata which has to be exchanged between the Service Provider and the Identity Provider.
<GeneralConfigDescription provider_account="OneLogin" />
![SAML Provider](/img/guides/zitadel_saml_create_provider.png)
After saving, open the newly created **OneLogin SAML provider** in ZITADEL.
In the details view you will find the **ZITADEL Service Provider** URLs, such as:
- **ZITADEL Metadata URL**
- **ZITADEL ACS Login Form URL** (for Login V1 and standard login flows)
- **ZITADEL Single Logout URL**
- **ZITADEL ACS Intent API URL** (for Login V2 and programmatic SAML flows)
For a typical browser-based login using the ZITADEL hosted login V2 you will use the:
- **ZITADEL ACS Intent API URL**, and
- **ZITADEL Metadata URL**.
You’ll need these URLs in the next step when finishing the OneLogin configuration.
### Complete OneLogin configuration with ZITADEL URLs
Now that ZITADEL is configured, return to OneLogin and finish the SAML configuration of the ZITADEL app.
1. In the OneLogin admin portal, open the **ZITADEL SAML** application.
2. Go to the **Configuration** tab (or the section where SAML SP settings such as ACS and Audience are configured).
3. Fill in the SAML settings using the values from the ZITADEL SAML provider:
- **ACS (Consumer) URL / SAML Consumer URL**
Set this to the **ZITADEL ACS Intent API URL**.
- **Recipient** (if present as a separate field)
Set this to the **ZITADEL ACS Intent API URL** as well.
- **Audience / SP Entity ID / SAML Audience**
Set this to the **ZITADEL Metadata URL**.
- **Single Logout URL** (optional but recommended)
Set this to the **ZITADEL Single Logout URL**.
- **ACS (Consumer) URL Validator** (if present)
For testing, you can use a permissive value like `.*`, then tighten it later to match the exact ACS URL.
![Image: OneLogin SAML configuration with ZITADEL URLs](/img/guides/onelogin-configuration.png)
4. Save your changes.
> _Optional: Import ZITADEL SP metadata_
> Some OneLogin SAML connectors provide an **Upload Metadata** or **Upload metadata file** option.
> In that case you can:
> - Download the SP metadata XML from the **ZITADEL Metadata URL**, and
> - Upload the XML file in OneLogin instead of manually filling in ACS and Audience.
> This will automatically populate many of the SAML settings and certificates.
5. Assign users or groups to the **ZITADEL SAML** application in OneLogin (for example via the **Users** or **Access** tab), so they can use it for SSO.
### Configure SAML attributes in OneLogin
To provision users correctly in ZITADEL, configure which attributes OneLogin sends in the SAML assertion.
1. In the OneLogin admin portal, open the **ZITADEL SAML** application.
2. Go to the **Parameters** tab.
3. Add or edit parameters so that at least the following are sent and **included in the SAML assertion**:
- `email` → mapped to the user’s email address (for example **Email**).
- `firstName` → mapped to the user’s given name (for example **First Name**).
- `lastName` → mapped to the user’s family name (for example **Last Name**).
4. Ensure the **NameID** is set to a unique identifier (commonly the user’s email address), if your OneLogin connector requires it.
5. Save your changes.
These attributes are later available in ZITADEL’s **IdP information** and can be mapped to ZITADEL user fields via Actions V2.
### Activate IdP
<Activate/>
![Activate OneLogin SAML provider in ZITADEL](/img/guides/zitadel-activate-onelogin-saml.png)
### Ensure your Login Policy allows External IdPs
<CustomLoginPolicy/>
## Configure an action to autofill user data
### Required for Login V2
The creation of users in ZITADEL will fail if the required fields to create a user are not set.
**Actions V2** can be used to map the SAML attributes returned by the IdP to the required fields in ZITADEL.
See the [Actions V2 Response Manipulation](/guides/integrate/actions/testing-response-manipulation.md)
guide for more information on setting up a Target and an Execution. In short,
* Create an Actions V2 Target of type `REST Call`.
* Create an Execution of type `Response` on the method `/zitadel.user.v2.UserService/RetrieveIdentityProviderIntent`.
The following minimal example modifies the response of `/zitadel.user.v2.UserService/RetrieveIdentityProviderIntent` to set the required fields
for user creation. This is just an example, please adjust the attributes according to your IdP.
``` go
package main
import (
"encoding/json"
"io"
"log"
"net/http"
"github.com/muhlemmer/gu"
"github.com/zitadel/zitadel-go/v3/pkg/client/zitadel/user/v2"
"google.golang.org/protobuf/encoding/protojson"
)
type contextResponse struct {
Request *retrieveIdentityProviderIntentRequestWrapper `json:"request"`
Response *retrieveIdentityProviderIntentResponseWrapper `json:"response"`
}
// RetrieveIdentityProviderIntentRequestWrapper necessary to marshal and unmarshal the JSON into the proto message correctly
type retrieveIdentityProviderIntentRequestWrapper struct {
user.RetrieveIdentityProviderIntentRequest
}
func (r *retrieveIdentityProviderIntentRequestWrapper) MarshalJSON() ([]byte, error) {
data, err := protojson.Marshal(r)
if err != nil {
return nil, err
}
return data, nil
}
func (r *retrieveIdentityProviderIntentRequestWrapper) UnmarshalJSON(data []byte) error {
return protojson.Unmarshal(data, r)
}
// RetrieveIdentityProviderIntentResponseWrapper necessary to marshal and unmarshal the JSON into the proto message correctly
type retrieveIdentityProviderIntentResponseWrapper struct {
user.RetrieveIdentityProviderIntentResponse
}
func (r *retrieveIdentityProviderIntentResponseWrapper) MarshalJSON() ([]byte, error) {
data, err := protojson.Marshal(r)
if err != nil {
return nil, err
}
return data, nil
}
func (r *retrieveIdentityProviderIntentResponseWrapper) UnmarshalJSON(data []byte) error {
return protojson.Unmarshal(data, r)
}
// call HandleFunc to read the response body, manipulate the content and return the response
func call(w http.ResponseWriter, req *http.Request) {
// read the body content
sentBody, err := io.ReadAll(req.Body)
if err != nil {
// if there was an error while reading the body return an error
http.Error(w, "error", http.StatusInternalServerError)
return
}
defer req.Body.Close()
// read the response into the expected structure
request := new(contextResponse)
if err := json.Unmarshal(sentBody, request); err != nil {
http.Error(w, "error", http.StatusInternalServerError)
return
}
// build the response from the received response
resp := request.Response
// manipulate the received response to send back as response
if err = manipulateResponse(resp); err != nil {
http.Error(w, "error modifying response", http.StatusInternalServerError)
return
}
// marshal the response into json
data, err := json.Marshal(resp)
if err != nil {
// if there was an error while marshalling the json
http.Error(w, "error marshaling response", http.StatusInternalServerError)
return
}
// return the manipulated response
w.Write(data)
}
type rawInformation struct {
Attributes struct {
Email []string `json:"email"`
FirstName []string `json:"firstName"`
Id []string `json:"id"`
LastName []string `json:"lastName"`
} `json:"attributes"`
Id string `json:"id"`
}
func manipulateResponse(resp *retrieveIdentityProviderIntentResponseWrapper) error {
if resp == nil || resp.IdpInformation == nil || resp.IdpInformation.RawInformation == nil {
log.Println("missing IdP/User information in the IdP response")
return nil
}
// retrieve raw information from the IdP
var rawInfoBytes []byte
var err error
if rawInfoBytes, err = resp.IdpInformation.RawInformation.MarshalJSON(); err != nil {
return err
}
// NOTE: the raw information struct used here is just an example.
// Please adapt this to your IdP.
var rawInfo rawInformation
if err = json.Unmarshal(rawInfoBytes, &rawInfo); err != nil {
return err
}
if len(rawInfo.Attributes.Email) == 0 ||
len(rawInfo.Attributes.FirstName) == 0 ||
len(rawInfo.Attributes.Id) == 0 ||
len(rawInfo.Attributes.LastName) == 0 {
log.Println("missing required attributes in the IdP response")
return nil
}
// to create a new user.
if resp.AddHumanUser != nil {
// set the required fields to create a new user in ZITADEL based on the information returned by the IdP
resp.IdpInformation.UserName = rawInfo.Attributes.Email[0]
resp.AddHumanUser.Profile = &user.SetHumanProfile{
GivenName: rawInfo.Attributes.FirstName[0],
FamilyName: rawInfo.Attributes.LastName[0],
}
resp.AddHumanUser.Email = &user.SetHumanEmail{
Email: rawInfo.Attributes.Email[0],
Verification: &user.SetHumanEmail_IsVerified{IsVerified: true},
}
resp.AddHumanUser.Username = gu.Ptr(rawInfo.Attributes.Email[0])
resp.AddHumanUser.IdpLinks = []*user.IDPLink{
{
UserName: rawInfo.Attributes.Email[0],
},
}
}
return nil
}
func main() {
// handle the HTTP call under "/call"
http.HandleFunc("/call", call)
// start an HTTP server with the before defined function to handle the endpoint under "http://localhost:8090"
http.ListenAndServe(":8090", nil)
}
```
## Test the setup
<TestSetup loginscreen="your OneLogin login"/>
![Image: OneLogin button on ZITADEL login](/img/guides/zitadel-login-onelogin-button.png)
+1
View File
@@ -386,6 +386,7 @@ module.exports = {
"guides/integrate/identity-providers/mocksaml",
"guides/integrate/identity-providers/okta-oidc",
"guides/integrate/identity-providers/okta-saml",
"guides/integrate/identity-providers/onelogin-saml",
"guides/integrate/identity-providers/openldap",
"guides/integrate/identity-providers/pingfederate-saml",
"guides/integrate/identity-providers/migrate",
Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB