Identity Provider-Initiated SAML Sign-In to OpenID Connect Applications

Overview

This article clarifies the process for an OpenID Connect (OIDC) application to work with an Identity Provider (IdP)-initiated Security Assertion Markup Language (SAML) sign-in flow. The issue manifests when the OIDC application rejects the unsolicited sign-in attempt or returns an error when presented with an opaque access_token instead of a JSON Web Token (JWT) format.

Applies To
  • Security Assertion Markup Language (SAML)
  • OpenID Connect (OIDC)
  • Enterprise Connections
Cause

The issue arises because any application that properly implements the OIDC/OAuth 2.0 (OAuth2) protocol rejects an unsolicited response from an Identity Provider (IdP). Additionally, Auth0 returns only an opaque (non-JSON Web Token (JWT)) access_token from IdP-initiated SAML flows.

Solution

Auth0 strongly recommends starting the login flow at the application in all cases. If this is not possible, the following method provides a workaround to complete an IdP-initiated SAML login, immediately followed by a seamless Service Provider (SP)-initiated SAML login, and return an OIDC response to the application.

NOTE: This method provides protection against Cross-Site Request Forgery (CSRF) attacks. Users do not see a login prompt because a session already exists from the IdP-initiated login that has just been completed. Users may see a consent prompt, which is expected.

The solution requires the application to implement and host a custom route handler, which should be accessible through a URL like https://example.com/startlogin.

 

Configuration

  1. Create an Application to represent the OIDC application in Auth0.

  2. Add the application’s custom route handler URL (for example, https://example.com/startlogin) to the Allowed Callback URLs for the application.

  3. Create a SAML Enterprise connection in Auth0 and configure it to work with the SAML IdP.

  4. Enable the SAML connection for the application created in step 1.

  5. In the SAML enterprise connection configuration, select the IdP-Initiated SSO tab.

  6. Under Default Application, select the application from step 1.

  7. For Response Protocol, select OpenID Connect.

  8. For Query String, construct a querystring parameter: Set redirect_uri to the custom route handler, plus a querystring indicating the name of the SAML connection. URL-encode this entire URL.

redirect_uri=https%3A%2F%2Fexample.com%2Fstartlogin%3Fconnection%3Dsamlidp1

 

IdP-Initiated SSO Behavior
  1. Determine how to start an IdP-initiated flow with the IdP. This varies by IdP and involves passing a URL to the IdP. A common pattern is to use the RelayState parameter.

  2. The IdP-initiated callback (Assertion Consumer Service (ACS)) URL for a SAML connection follows this pattern: https://<mytenant.auth0.com>/login/callback?connection=<MY_CONNECTION_NAME>.


Flow

This is a sequence diagram showing the entire sign-in flow. Certain steps are highlighted in detail later.

 

Notes

  • Starting in (8) this is simply a regular OIDC sign-in flow, with an SP-initiated interaction with the SAML IdP.
  • This example uses the implicit flow for the sake of simplicity, but the application can request any supported OIDC flow in step 8 (authorize code flow, PKCE, etc.).
  • In (7) the application simply “throws away” the response from Auth0, reads the connection parameter from the request, and creates an internal state for the authentication transaction.
  • (4) through (15) require no interaction from the user (with the possible exception of a consent prompt from Auth0).
  • In (15) the application reads the state parameter returned in the response from Auth0. This state parameter must match the internal state generated in (7). This is a critical part of the OIDC security model.

sign in flow

Route Handler Implementation

The route handler should generally have a different URL than the standard /login endpoint. The endpoint should call the same login() method as the /login endpoint. The existing login() method may require an update to accept and relay the connection parameter to the Auth0 tenant.

The route handler must Parse the connection name from the query string and then Pass it to the login() method, which generates an appropriate /authorize request. This request includes the redirect_uri to the application and a connection parameter (the connection parameter is key to this solution).

Example Modification (Using auth0-spa-js SDK): The modified router to include a new /startlogin route handler:

const router = 
  "/": () => showContent("content-home"),
  "/profile": () =>
    requireAuth(() => showContent("content-profile"), "/profile"),
  "/login": () => login()
};

const login = async (targetUrl) => {
  try {
    const options = {
      redirect_uri: window.location.origin
    };

    if (targetUrl) {
      options.appState = { targetUrl };
    }
    await auth0.loginWithRedirect(options);
  } catch (err) {
    console.log("Log in failed", err);
  }
};
 

The new startlogin method to retrieve the connection parameter:

const router = {
  "/": () => showContent("content-home"),
  "/profile": () =>
    requireAuth(() => showContent("content-profile"), "/profile"),
  "/login": () => login(),
  "/startlogin": () => startlogin()
};

//new method to start login from idp-initiated callback
const startlogin = async () => {
  console.log(window.location.href)
  let myURL = new URL(window.location.href);
  let conn = myURL.searchParams.get("connection");
  return  login(null, conn);
}

/**
 * Starts the authentication flow
 */
const login = async (targetUrl, connection) => {
  try {
    console.log("Logging in", targetUrl);

    const options = {
      redirect_uri: window.location.origin,
    };

    if (connection) {
      options.connection = connection;
    }

    if (targetUrl) {
      options.appState = { targetUrl };
    }

    await auth0.loginWithRedirect(options);
  } catch (err) {
    console.log("Log in failed", err);
  }
};

Any implementation must Pass a state value to the /authorize URL for CSRF protection and Validate the state in the response from the Identity Provider (IdP).

Recommended content

No recommended content found...