Quasr
  • Introduction
    • Welcome to Quasr
    • Concepts
      • Flexible Authentication
      • User-Centric Privacy
      • Modern Development
    • Terminology
    • FAQs
  • Getting Started
    • Sign up with Quasr
    • Setup your tenant
      • Factor Configuration
      • Enrolling additional factors
      • Test with the Sample Client
      • Understanding Scopes & Scores
      • Setting up an API Client (M2M)
    • Connect your app
      • Hosted Login UI
      • Custom Login UI
      • Embedded Login UI
  • Account Administration
    • Introduction
    • Account & Billing
      • Metrics
    • Tenants
    • Usage & Statistics
    • Security
  • Tenant Administration
    • Introduction
    • Dashboard
    • Tenant Settings
    • Your Security
    • Accounts
      • Tenant Admins
    • Factors
      • Factors and Scoring
      • Username (ID)
      • Identity Provider (IDP)
        • Apple
        • Facebook
        • GitHub
        • Google
        • LinkedIn
        • Slack
      • Time-based One-time Password (TOTP)
      • One-Time Password (OTP)
      • Password
      • Secret
    • Controls
      • Configuration
      • Permissions
      • Consents
      • Rules
    • Attributes
      • Capturing Claims
      • Sourcing Claims
      • Viewing Claims
      • Searching Claims / Users
      • Sharing Claims
    • Extensions
      • Synchronous
      • Asynchronous
    • Tokens
      • Session Token (OAuth 2.0)
      • Access Token (OAuth 2.0)
      • Refresh Token (OAuth 2.0)
      • ID Token (OIDC 1.0)
      • Consent Token
      • Authorization Code (OAuth 2.0)
    • Hosted Login Page
    • APIs
      • Authentication API
      • Management API (GraphQL)
  • Legal
    • Terms of Service
    • Acceptable Use Policy
    • DPA & Subprocessors
  • More Info
    • Standards
    • Security
      • Vulnerability Disclosure
      • Wall of Recognition
    • Support
    • Status
Powered by GitBook
On this page
  • Configuration
  • Setup
  • Option 1: Redirect
  • Option 2: Separate Window & Polling
  • State Reuse
  • Enrollment Not Found
  • Enrollment Not Bound
  • Enrollment Mismatch
  • Enrollment Already Exists
  • Account Already Exists
  1. Tenant Administration
  2. Factors

Identity Provider (IDP)

PreviousUsername (ID)NextApple

Last updated 9 days ago

Accounts can use this factor to authenticate with an Identity Provider (IDP) - which is an online service provider that provides identities. The IDP will perform its own user authentication, and can also provide user claims (attributes) which can be captured in Quasr using sources (see Capturing Claims). At this moment IDPs that support the following can be configured:

  1. OpenID Connect 1.0 (OIDC)

  2. OAuth 2.x + API that provides claims using access token (similar to the UserInfo endpoint in OIDC)

We don't provide support for SAML IDPs.

Besides a generic factor allowing you to configure any OAuth 2.0 / OIDC-compliant provider following IDPs are supported out-of-the-box:

  • Microsoft

  • Discord

  • Quasr

We're always keen to understand which IDPs you'd like to see us support out-of-the-box that we're currently missing. Please feel free to inquire or request support via .

Below is a sample screenshot to give an idea of a potential login / registration page offering Identity Provider (IDP) options. On a login or registration page the IDP is often represented by an icon (logo).

Configuration

An IDP factor has following additional configuration:

  • Redirect URIs - These are the allowed redirect URIs where a user can be redirected to after they've finished with the IDP. This will typically be your login and account UI. If no redirect URI is requested specifically the following happens:

    1. If the origin of the request is within the list of allowed redirect URIs this will be used.

    2. If no match the first allowed redirect URI will be used.

  • Client ID - This is the client ID as received from the Identity Provider (IDP) upon configuration.

  • Authorization Endpoint - This is the OAuth 2.0 authorization endpoint where an initial login request must be send to and is pre-configured for out-of-the-box providers.

  • Scope - These are the scopes to request from the provider, and will determine which user claims are returned upon successful login. Including the openid scope indicates support for OpenID Connect.

  • Response Type - This is what the IDP must return after a successful login/signup. This can be one of the following options and is pre-configured for out-of-the-box providers:

    • Identity Token - Meaning that an identity token containing user claims is returned, immediately as part of the redirect. This is quicker but less secure as the client - in this case Quasr - does not need to authenticate.

    • Authorization Code - Meaning only a code is returned that must be exchanged for tokens which takes more steps but is more secure, as the client should need to authenticate for the exchange.

    • Authorization Code & Identity Token - Meaning both an identity token as well as code should be returned. This is processed identically as if only an identity token is received.

    • No Preference - Meaning the IDP can decide.

  • Response Mode - This is how the IDP should return the authorization code and/or identity token but it could be ignored. This can be one of the following options and is pre-configured for out-of-the-box providers:

    • Query Parameters (GET) - Meaning that the assets are provided using query parameters.

    • Form Submission (POST) - Meaning that the assets are provided in a from-encoded body. This option is considered more secure than using query parameters.

    • No Preference - Meaning the IDP can decide.

  • Signed Request - This indicates whether requests should be signed by Quasr and is pre-configured for out-of-the-box providers. For example Quasr as IDP requires signed request which is considered more secure.

  • Token Endpoint - This is the OAuth 2.0 token endpoint used to exchange an authorization code for tokens and is pre-configured for out-of-the-box providers.

  • Content-Type - This is the content type used by the token endpoint. This can be one of the following options and is pre-configured for out-of-the-box providers:

    • Form URL-encoded (application/x-www-form-urlencoded)

    • JSON (application/json)

  • Code Challenge Method - This is the code challenge method used for Proof Key for Code Exchange (PKCE) which is highly recommended and even required by the OAuth 2.1 standard. This can be one of the following options and is pre-configured for out-of-the-box providers:

    • Cleartext - Meaning the code challenge is send in clear.

    • Hash - Meaning the code challenge is initially send in hashed format and only in clear upon the code exchange. This option is considered more secure.

    • None - Meaning no code challenge is used. This option is not recommended.

  • Client Authentication - This is the client authentication method, i.e. how Quasr will authenticate to the token endpoint to exchange an authorization code. This can be one of the following options and is pre-configured for out-of-the-box providers:

    • Client Secret (Authorization Header) - Meaning the client secret is provided as the password by the Basic Auth scheme in the Authorization header.

    • Client Secret (Body) - Meaning the client secret is provided in the request body.

    • Private Key (JWT) - Meaning the client will provide a signed assertion (JWT) using its private key in the request body. For example Quasr as IDP requires private key client authentication which is considered more secure.

  • Client Secret - In case the client must authenticate using a client secret, this must contain the client secret as received from the Identity Provider (IDP) upon configuration.

  • Private / Public Key - In case the client must authenticate using a private key, this must contain the private / public key pair in PEM-encoded format.

Client secrets and private keys are securely stored in encrypted format.

  • Issuer - This is the issuer of the identity token and must match the iss claim and is pre-configured for out-of-the-box providers.

  • JWKS Endpoint - This is the endpoint providing the public keys in JWKS format by which the identity token is signed and is pre-configured for out-of-the-box providers.

  • User Info Endpoint - This is the endpoint providing user claims with an access token and is only used if no identity token was returned. The endpoint is pre-configured for out-of-the-box providers.

Hence you can configure an OAuth 2.0 IDP not supporting OIDC by providing a user info endpoint that can be used to obtain claims.

  • Nonce - This indicates whether a nonce must be included in the request that must be returned in the identity token and match the nonce claim. Using is nonce is more secure and highly recommended. This setting is pre-configured for out-of-the-box providers.

This setting must be enabled for sources on the factor to work. Note that bidirectional sources will still work in reserve even if this setting is disabled.

  • Source - This is an optional setting that allows soft checking a source upon enrollment. This setting is only useful if you configure a source for a unique attribute. If during enrollment the source finds a collision, i.e. another account already holds the respective claim, then the enrollment will fail even if no collision was found with the enrollment itself. In this case the authorization state can be re-used with the collision account (see State Reuse).

Setup

There are 2 ways in which an Identity Provider (IDP) factor can be implemented:

Option 1: Redirect

In this setup the user is redirected away from the login or account UI towards the Identity Provider (IDP) hence when returning the login or account UI will be reloaded. This is the most efficient setup, but it will redirect the user away from your interfaces.

Step 1: Trigger Factor / Enrollment

// POST https://{{tenant_id}}.api.quasr.io/factors
// Authorization: Bearer <session_token> (optional)
[
    {
        "id": "<factor_id|enrollment_id>", // REQUIRED
        "input": "redirect_uri" // RECOMMENDED
    }
]

The input provided is the requested redirect URI, i.e. the URL where the user should be redirected to after coming back from the IDP. The response looks as follows (assuming the provided redirect URI was valid):

{
    "result": "PENDING",
    "feedback": {
        "cause": "OAUTH2_PENDING",
        "authorization_url": "<authorization_url>",
        "authorization_state": "<state_id>",
        ...
    }
}

Step 2: Redirect User

Now redirect the user to the Authorization URL as received in step 1 and make sure to save the session if already present - so it can be restored upon return.

Step 3: Handle Response

After the user finishes with the IDP they'll be redirected back to the requested redirect URI. On this page you'll receive two query parameters, id and input, which you must use to call the Authentication API again. Use the received query parameters for the respective fields and use the session (if it was present).

The id query parameter is in fact the same factor or enrollment ID as used in step 1 and the input query parameter is in fact the authorization state ID, as received in step 1. Hence you could ignore the parameters if you kept the relevant state.

Depending on the outcome you'll either receive a SUCCESS response with a (upgraded) session token or a FAILED response with feedback details.

Option 2: Separate Window & Polling

In this setup the user is presented a new window (popup/embedded) in which they are directed towards the Identity Provider (IDP). The original - now in the background - window will regularly poll to check for completion. This setup is less efficient due to polling but will keep your interface open with no reloading.

Step 1: Trigger Factor / Enrollment

// POST https://{{tenant_id}}.api.quasr.io/factors
// Authorization: Bearer <session_token> (optional)
[
    {
        "id": "<factor_id|enrollment_id>", // REQUIRED
        "input": "redirect_uri" // RECOMMENDED
    }
]

The input provided is the requested redirect URI, i.e. the URL where the user should be redirected to after coming back from the IDP. The response looks as follows (assuming the provided redirect URI was valid):

{
    "result": "PENDING",
    "feedback": {
        "cause": "OAUTH2_PENDING",
        "authorization_url": "<authorization_url>",
        "authorization_state": "<state_id>",
        ...
    }
}

Step 2: Open Window

Now open a new window (either popup or embedded) using the Authorization URL as received in step 1. When performing step 1 make sure that the requested redirect URI is a page that closes the window, as it's no longer required after completion of the flow.

Step 3: Check Progress

To check on the progress of the user in the new window (popup/embedded) in the background window you can regularly call/poll the API by providing the authorization state ID as received in step 1 as input:

// POST https://{{tenant_id}}.api.quasr.io/factors
// Authorization: Bearer <session_token> (optional)
[
    {
        "id": "<factor_id|enrollment_id>", // REQUIRED
        "input": "<state_id>" // REQUIRED
    }
]

In case the user hasn't completed the flow yet you'll receive the following response (which is identical to the initial response):

{
    "result": "PENDING",
    "feedback": {
        "cause": "OAUTH2_PENDING",
        "authorization_url": "<authorization_url>",
        "authorization_state": "<state_id>",
        ...
    }
}

Please note that each call counts as an unsuccessful attempt hence you must take care to not get blocked:

  • in case of an enrollment the maximum is 5 attempts before the enrollment is temporarily locked

  • in case of a factor the maximum is 20 attempts before your IP is temporarily locked

State Reuse

It's possible in certain cases to re-use the authorization state even if the flow failed:

  • At login when no enrollment is found the state can be re-used for a new enrollment either for a new account (ENROLLMENT_NOT_FOUND) or, if a source indicates another account already holds a claim, for the respective account (ENROLLMENT_NOT_BOUND).

  • At login when the enrollment doesn't match the provided enrollment (ENROLLMENT_MISMATCH) the state can be re-used for validation of the respective enrollment.

  • At signup when the enrollment already exists (ENROLLMENT_ALREADY_EXISTS) the state can be re-used for validation of the respective enrollment.

  • At signup when a source indicates another account already has claim (ACCOUNT_ALREADY_EXISTS) the state can be re-used for a new enrollment on the respective account.

Enrollment Not Found

This case can occur during login when no enrollment was found for a successful IDP flow. If a source is configured then the source did also not find another account holding a claim. This most likely indicates that the user trying to login is in fact new and should signup instead... but it could also mean the user in fact has an account but with another claim (for example another email). Hence authorization state can be re-used with a new signup or new enrollment on an existing account.

The response will look as follows:

{
    "result": "FAILED",
    "feedback": {
        "cause": "ENROLLMENT_NOT_FOUND",
        "authorization_state": "<state_id>", // STATE CAN BE RE-USED
        "authorization_mode": "signup", // NOTICE THE CHANGE TO SIGNUP
        "expires_at": "2025-12-31T13:28:23.000Z", // WHEN THE STATE EXPIRES
        "claims": {} // ONLY RETURNED IF CLAIMS CAPTURE IS ENABLED AND COULD BE USED TO FAST-TRACK A SIGNUP
    }
}

Option 1: New Account

After the initial POST /controls call the state could be re-used to signup a new account as follows:

// POST https://{{tenant_id}}.api.quasr.io/factors
// Authorization: Bearer <session_token>
[
    {
        "id": "<factor_id>", // ID OF RESPECTIVE FACTOR
        "input": "<state_id>" // STATE ID AS RECEIVED IN FAILED RESPONSE
    }
]

The main advantage is that user does not need to go back to IDP to signup, although the IDP most likely remembered their previous session/consent and would be immediately redirected back, this can now be done invisible in the background.

Option 2: Existing Account

To add an enrollment to an existing account you must first let the user login to some client that obtains the relevant account scope. During this process you must make sure to keep the relevant state ID. Once obtained you can call the Management API as follows:

// POST https://{{tenant_id}}.api.quasr.io/graphql
// Authorization: Bearer <accesss_token>
{
    "query": `
        mutation createEnrollment($input: CreateEnrollmentInput!) {
            createEnrollment(input: $input) {
                id
            }
        }`,
    "variables": `{
        "input": {
            "factor": "<factor_id>", // REQUIRED
            "subtype": "oauth2:xxx", // REQUIRED
            "input": "<state_id>", // OPTIONAL
            ...
        }
    }`
}

Enrollment Not Bound

This case can occur during login when no enrollment was found for a successful IDP flow but a source is configured and found another account holding claim. Hence this case is very similar to the previous one, but now we know that an existing account is already present.

The response will look as follows:

{
    "result": "FAILED",
    "feedback": {
        "cause": "ENROLLMENT_NOT_BOUND",
        "account_id": "<account_id>", // THE ACCOUNT THE SOURCE FOUND
        "authorization_state": "<state_id>", // STATE CAN BE RE-USED
        "authorization_mode": "signup", // NOTICE THE CHANGE TO SIGNUP
        "expires_at": "2025-12-31T13:28:23.000Z", // WHEN THE STATE EXPIRES
        "claims": {} // ONLY RETURNED IF CLAIMS CAPTURE IS ENABLED AND COULD BE USED TO FAST-TRACK A LOGIN
    }
}

The response is very similar to ENROLLMENT_NOT_FOUND but now an account_id field is returned and the state is bound to this account. Hence you can't re-use the state freely but only for the identified account.

To add an enrollment to an existing account you must first let the user login to some client that obtains the relevant account scope. During this process you must make sure to keep the relevant state ID. Once obtained you can call the Management API as follows:

// POST https://{{tenant_id}}.api.quasr.io/graphql
// Authorization: Bearer <accesss_token>
{
    "query": `
        mutation createEnrollment($input: CreateEnrollmentInput!) {
            createEnrollment(input: $input) {
                id
            }
        }`,
    "variables": `{
        "input": {
            "factor": "<factor_id>", // REQUIRED
            "subtype": "oauth2:xxx", // REQUIRED
            "input": "<state_id>", // OPTIONAL
            ...
        }
    }`
}

Enrollment Mismatch

This case can occur during login using a specific enrollment... but the enrollment found is not the same. Hence the state can now be re-used with the found enrollment.

The response will look as follows:

{
    "result": "FAILED",
    "feedback": {
        "cause": "ENROLLMENT_MISMATCH",
        "enrollment_id": "<enrollment_id>", // THE ENROLLMENT FOUND
        "authorization_state": "<state_id>", // STATE CAN BE RE-USED
        "authorization_mode": "login", // NOTICE IT REMAINS IN LOGIN
        "expires_at": "2025-12-31T13:28:23.000Z", // WHEN THE STATE EXPIRES
        "claims": {} // ONLY RETURNED IF CLAIMS CAPTURE IS ENABLED AND COULD BE USED TO FAST-TRACK A LOGIN
    }
}

The state can now be re-used as follows:

// POST https://{{tenant_id}}.api.quasr.io/factors
// Authorization: Bearer <session_token> (optional)
[
    {
        "id": "<enrollment_id>", // ENROLLMENT ID AS RECEIVED IN FAILED RESPONSE
        "input": "<state_id>" // STATE ID AS RECEIVED IN FAILED RESPONSE
    }
]

Note that this error can be avoided by triggering the login on the factor ID instead of the enrollment ID.

Enrollment Already Exists

This case can occur during signup is in fact identical to the more common RESERVED_INPUT failure but semantically not a good fit, as the input is not coming from the user but from the IDP. This indicates the user is trying to signup but already has an account with the respective enrollment and should login. Now the state can be re-used to login the user.

The response will look as follows:

{
    "result": "FAILED",
    "feedback": {
        "cause": "ENROLLMENT_ALREADY_EXISTS",
        "enrollment_id": "<enrollment_id>", // THE ENROLLMENT FOUND
        "authorization_state": "<state_id>", // STATE CAN BE RE-USED
        "authorization_mode": "login", // NOTICE THE CHANGE TO LOGIN
        "expires_at": "2025-12-31T13:28:23.000Z", // WHEN THE STATE EXPIRES
        "claims": {} // ONLY RETURNED IF CLAIMS CAPTURE IS ENABLED AND COULD BE USED TO FAST-TRACK A LOGIN
    }
}

The state can now be re-used as follows:

// POST https://{{tenant_id}}.api.quasr.io/factors
// Authorization: Bearer <session_token> (optional)
[
    {
        "id": "<enrollment_id>", // ENROLLMENT ID AS RECEIVED IN FAILED RESPONSE
        "input": "<state_id>" // STATE ID AS RECEIVED IN FAILED RESPONSE
    }
]

Account Already Exists

This case can occur during signup when no enrollment was found in a successful IDP flow, but a source found another account holding a claim. This most likely indicate the user already has an account and in fact must login - but using other means. The claims returned could help fast-track a login if for example an email is returned which serves as a username. Hence the state can now be re-used, but only with the respective account found.

The response will look as follows:

{
    "result": "FAILED",
    "feedback": {
        "cause": "ACCOUNT_ALREADY_EXISTS",
        "account_id": "<account_id>", // THE ACCOUNT THE SOURCE FOUND
        "authorization_state": "<state_id>", // STATE CAN BE RE-USED
        "authorization_mode": "signup", // NOTICE IT REMAINS IN SIGNUP
        "expires_at": "2025-12-31T13:28:23.000Z", // WHEN THE STATE EXPIRES
        "claims": {} // ONLY RETURNED IF CLAIMS CAPTURE IS ENABLED AND COULD BE USED TO FAST-TRACK A LOGIN
    }
}

To add an enrollment to an existing account you must first let the user login to some client that obtains the relevant account scope. During this process you must make sure to keep the relevant state ID. Once obtained you can call the Management API as follows:

// POST https://{{tenant_id}}.api.quasr.io/graphql
// Authorization: Bearer <accesss_token>
{
    "query": `
        mutation createEnrollment($input: CreateEnrollmentInput!) {
            createEnrollment(input: $input) {
                id
            }
        }`,
    "variables": `{
        "input": {
            "factor": "<factor_id>", // REQUIRED
            "subtype": "oauth2:xxx", // REQUIRED
            "input": "<state_id>", // OPTIONAL
            ...
        }
    }`
}

Capture Tokens - If enabled all tokens will be captured and propagated as data event. Please note that this feature is experimental and due to the nature of the implementation not always an access token is obtained. You can find the event format .

Capture Claims - If enabled all user claims returned will be captured and propagated as data event. You can find the event format .

The API call to trigger the IDP factor or enrollment looks as below (Postman documentation ):

The API call to trigger the IDP factor or enrollment looks as below (Postman documentation ):

here
here
here
here
Redirect
Separate Window & Polling
Google
Apple
Facebook (Meta)
LinkedIn
Slack
GitHub
support@quasr.io
Drawing
Drawing