SAML Login to OIDC Site

Greetings -

We have a site that’s using Keycloak for user accounts. We’re using OIDC, so users sign in with Keycloak and get directed to our site, which gets the access and refresh tokens and continues on from there.

Now we’re looking at building an integration with a partner, who wants SSO based on IdP-initiated SAML.

I’m not an expert, but I gather this is identity brokering. So the user would authenticate with SAML, perhaps do the on-the-fly registration based on the flow, but then what?

Will Keycloak create the OIDC token and redirect the user to the service with the access and refresh tokens similar to a normal user login? (Can it?)

We’d rather not add SAML support to the website if we don’t have to, but we need to make this integration work.

Thanks
– Steve

1 Like

You can leave your application code that uses OIDC as-is. Add the SAML IdP, and a client to enable IdP-initiated login. When the user logs in via the SAML SSO provider and is redirected to Keycloak, Keycloak will redirect to your app with a normal code-to-token OIDC flow.

It’s somewhat dated, and specific to Okta, but I’ve found this to be a good guide for a similar setup:v
https://www.lisenet.com/2020/keycloak-with-okta-idp-initiated-sso-login/

Hi Garth - thanks for responding.

I did find and follow that guide, and I have it set up so I can use an SP initiated login, but when I attempt to log in via the Okta embed link I get:

WARN  [org.keycloak.events] (executor-thread-954) type=IDENTITY_PROVIDER_LOGIN_ERROR, realmId=myrealm-dev, clientId=null, userId=null, ipAddress=184.146.1
32.175, error=invalid_code 

I believe Keycloak is trying to parse things out of the RelayState instead of using it as a redirect url. Not sure where to go from here. Any suggestions?

Thanks
– Steve

Not off hand. If you can post the SAML and client config, it would be helpful for us to debug.

I’m not sure what to share … here’s the client export:

{
  "clientId": "okta-demo-client",
  "name": "",
  "description": "",
  "rootUrl": "",
  "adminUrl": "",
  "baseUrl": "",
  "surrogateAuthRequired": false,
  "enabled": true,
  "alwaysDisplayInConsole": false,
  "clientAuthenticatorType": "client-secret",
  "secret": "zzz",
  "redirectUris": [
    "https://mysite.dev.devca.mycorp.dev",
    "https://keycloak.devca.mycorp.dev/realms/fusion-dev/broker/okta-demo-saml/endpoint"
  ],
  "webOrigins": [
    "https://keycloak.devca.mycorp.dev"
  ],
  "notBefore": 0,
  "bearerOnly": false,
  "consentRequired": false,
  "standardFlowEnabled": true,
  "implicitFlowEnabled": false,
  "directAccessGrantsEnabled": false,
  "serviceAccountsEnabled": false,
  "publicClient": false,
  "frontchannelLogout": true,
  "protocol": "saml",
  "attributes": {
    "saml_idp_initiated_sso_relay_state": "https://mysite.dev.devca.mycorp.dev",
    "saml.force.post.binding": "true",
    "post.logout.redirect.uris": "https://mysite.dev.devca.mycorp.dev",
    "saml.server.signature.keyinfo.ext": "false",
    "saml.signature.algorithm": "RSA_SHA256",
    "saml.client.signature": "false",
    "saml.force.name.id.format": "false",
    "saml.allow.ecp.flow": "false",
    "saml.assertion.signature": "false",
    "client.secret.creation.time": "1686935252",
    "saml_single_logout_service_url_post": "https://keycloak.devca.mycorp.dev/realms/fusion-dev/broker/okta-demo-saml/endpoint",
    "saml.encrypt": "false",
    "saml_assertion_consumer_url_post": "https://mysite.dev.devca.mycorp.dev",
    "saml.server.signature": "true",
    "saml_idp_initiated_sso_url_name": "okta-demo-client",
    "saml.artifact.binding.identifier": "zzz",
    "saml.artifact.binding": "false",
    "saml_force_name_id_format": "false",
    "saml.server.signature.keyinfo$xmlSigKeyInfoKeyNameTransformer": "NONE",
    "saml.authnstatement": "true",
    "display.on.consent.screen": "false",
    "saml_name_id_format": "email",
    "saml_signature_canonicalization_method": "http://www.w3.org/2001/10/xml-exc-c14n#",
    "saml.onetimeuse.condition": "false"
  },
  "authenticationFlowBindingOverrides": {},
  "fullScopeAllowed": true,
  "nodeReRegistrationTimeout": -1,
  "defaultClientScopes": [
    "role_list"
  ],
  "optionalClientScopes": [],
  "access": {
    "view": true,
    "configure": true,
    "manage": true
  }
}

That steps on that page have us creating a client that points to the the identity provider in Keycloak … here’s the metadata for that client:

<md:EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" entityID="https://keycloak.devca.mycorp.dev/realms/fusion-dev" ID="ID_b2d3d4c8-39a2-47fe-96ea-45458f852742">
<md:SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol" AuthnRequestsSigned="false" WantAssertionsSigned="false">
<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://keycloak.devca.mycorp.dev/realms/fusion-dev/broker/okta-demo-saml/endpoint"/>
<md:NameIDFormat>
urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
</md:NameIDFormat>
<md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://keycloak.devca.mycorp.dev/realms/fusion-dev/broker/okta-demo-saml/endpoint" isDefault="true" index="1"/>
</md:SPSSODescriptor>
</md:EntityDescriptor>

And the realm’s metadata:

<md:EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" entityID="https://keycloak.devca.mycorp.dev/realms/fusion-dev">
<md:IDPSSODescriptor WantAuthnRequestsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<md:KeyDescriptor use="signing">
<ds:KeyInfo>
<ds:KeyName>zzz</ds:KeyName>
<ds:X509Data>
<ds:X509Certificate>
cert...
</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</md:KeyDescriptor>
<md:ArtifactResolutionService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://keycloak.devca.mycorp.dev/realms/fusion-dev/protocol/saml/resolve" index="0"/>
<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://keycloak.devca.mycorp.dev/realms/fusion-dev/protocol/saml"/>
<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://keycloak.devca.mycorp.dev/realms/fusion-dev/protocol/saml"/>
<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" Location="https://keycloak.devca.mycorp.dev/realms/fusion-dev/protocol/saml"/>
<md:NameIDFormat>
urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
</md:NameIDFormat>
<md:NameIDFormat>
urn:oasis:names:tc:SAML:2.0:nameid-format:transient
</md:NameIDFormat>
<md:NameIDFormat>
urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
</md:NameIDFormat>
<md:NameIDFormat>
urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
</md:NameIDFormat>
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://keycloak.devca.mycorp.dev/realms/fusion-dev/protocol/saml"/>
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://keycloak.devca.mycorp.dev/realms/fusion-dev/protocol/saml"/>
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://keycloak.devca.mycorp.dev/realms/fusion-dev/protocol/saml"/>
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" Location="https://keycloak.devca.mycorp.dev/realms/fusion-dev/protocol/saml"/>
</md:IDPSSODescriptor>
</md:EntityDescriptor>

On the Okta side, the SSO url is
https://keycloak.devca.cloudmd.dev/realms/fusion-dev/broker/okta-demo-saml/endpoint/clients/okta-demo-client

When I go to the Okta embed link, or use an SP-initiated login from the login page, I get prompted for my Okta credentials, I enter them, get redirected back to Keycloak, and get “Invalid username or password.”. There’s an error in the console:

type=IDENTITY_PROVIDER_FIRST_LOGIN_ERROR, realmId=fusion-dev, clientId=okta-demo-client, userId=null, ipAddress=184.146.132.175, error
=invalid_user_credentials, identity_provider=okta-demo-saml, auth_method=saml, redirect_uri=https://kii.dev.devca.cloudmd.dev, identity_provider_identity=myemail@gmail.com, code_id=cf88a3f3-876c-4d96-8d
5b-2ea503e5e55e, authSessionParentId=cf88a3f3-876c-4d96-8d5b-2ea503e5e55e, authSessionTabId=ORfowrKc-Zs

(That’s different than what I posted in my last message - I recreated the client since then, not sure if this is farther along or not).

Thanks
– Steve

I got past this. The fix for the “Invalid username or password” error was to set the Name ID Format in Okta to email.

What happens now is the login seems to complete successfully, but the user ends up on the Keycloak page saying the user is already logged in.

If I manually load the final SP page at this point, it loads and the user is indeed already logged in, but I can’t figure out how to get the user redirected there after the login. I’ve tried putting the SP URL in the RelayState, but that didn’t have any effect.

Thanks
– Steve