[FEATURE-13476,14109,16160] Okta OIDC illegal_custom_scope

I’ve just setup a brand-new Okta tenant, and am trying to set it up as an Identity Provider in Ignition using OIDC.

I went through all the steps in the OIDC example in the manual. The base case works fine, and I can login/logout properly. However, as soon as I add any scope values to the IdP config, the login test fails.

I’m trying this with a single scope named “email”.

Looking in Ignition’s logs, I see a “Unable to handle login response” error from the gateway.WebAuthSessionImpl component.

Looking in Okta’s logs, I see a matching report about “OIDC authorization request FAILURE : illegal_custom_scope”.

I followed the steps in the manual which covered the Ignition side thoroughly, so I’m guessing this must be something on the Okta side, but I’m having no luck finding any hints searching Okta configuration and documentation yet. I’m hoping this is something really simple that someone in this forum could help me with.

1 Like

Is there any chance “email” is a reserved word for this purpose (shadowing a standard attribute)?

“email” is one of the pre-defined and guaranteed to be there scope values from Okta: https://developer.okta.com/docs/api/resources/oidc#scope-dependent-claims-not-always-returned

This is before I try to map anything anywhere in Ignition, and purely in terms of what information Ignition is trying to request from the IdP.

Try setting the gateway loggers gateway.WebAuthSessionImpl and gateway.OIDCWebAuthStrategy to debug and login again. There may be a message like this:

Unable to parse the web auth response from the HTTP request URI:

Whatever follows the colon character may give some helpful debugging information from Okta’s side

I mis-spoke in my initial post: this was failing when trying to request the scope “name”. I just tried again with the scope “email” and it worked. That got me scratching my head.

Then I (finally) noticed the second column in the table of that Okta documentation: “required scope”. To get the “email” property I had to request “email” scope since that was the required scope. To get the “name” property I requested the “profile” scope.

I somehow missed the fact that the values I was supposed to be putting in as the “scope” were the second column on that Okta table, not the first.

Now, I’ve added the scope names “profile”, “email”, “phone”, and “groups”. I can successfully do test logins with this setup, and more information is showing up in the IDP response, but still not everything I was expecting. I’m not seeing any phone numbers, groups, or separate first/last names…

I’m past the thing I was stuck on, but not out of the woods. I’m going to keep reading and experimenting. Suggestions are welcome, and I’ll update here if I figure out my new issue.

1 Like

I think I found my issue a bit further down in the Okta docs. (Note that a “claim” is a user property field in this context…)


Ignition must be forming the authentication request in one of the two highlighted forms from the table. I suspect I could get the full set of user properties if I could convince Ignition to use a different method or do an API call to the userinfo endpoint.

Hrm. From what I can tell, Ignition never exposes the access token I would need to generate the API request to the userinfo endpoint myself.

I don’t know enough about OIDC to know if that endpoint is a standard part of the API or Okta-proprietary, but I’m sort of dead in the water without the ability to get the other Okta user properties in Ignition. My intended use case depends on at least the groups, and it would be nice to have the other fields as well.

Is there any way around this? Is there a chance Ignition could pull in more fields from that endpoint to map into the user properties?

Unfortunately if you cannot get the information you need in the id token itself, then you will be stuck for now. We do have two existing feature tickets on our backlog which would solve your problem:

13476 - Expose the access / id tokens from OIDC IdPs
14109 - First class support for the User Info API

I’ve linked this thread to those tickets in order to increase the priority

I’ve got an Okta-specific workaround. It depends on that “Org AS” versus “Custom AS” differentiation from the Okta docs above… (“AS” is authentication server in this context)

Rough steps to resolve (working from recent memory, but not actually stepping through this… sorry if I mess something up):

  1. On your Okta admin console, go to Security -> API -> Authorization Servers
  2. Find the “Issuer URI” for the default AS
  3. Go back to the IdP settings in Ignition
  4. Under “Import from URL” put in “{issuer_uri}/.well-known/openid-configuration” (e.g. your issuer URI will be something like “https://mycompany.okta.com/oauth2/default” so your import URL would be “https://mycompany.okta.com/oauth2/default/.well-known/openid-configuration”)
  5. Import that config
  6. Make sure your requested scopes are “profile” and “phone” and “email” and “address”. (Maybe not required, but it didn’t seem to hurt, and it will be useful when IA fixes issue 14109)
  7. Save the IdP config
  8. Back in Okta, click on the edit icon to the right of the “default” AS
  9. Click on “Claims”
  10. For each property you want to ensure is in the ID token, click “Add Claim”, be sure to set it to “Include in ID token always” and figure out the right value settings to get the property you want. You can use “appuser.prop” to get fields that are set in the application property mappings. I gave mine unique names that didn’t already exist anywhere else, but I’m not sure if that was necessary. (Seemed the safer route)
  11. Head back to Ignition do an IdP test login, and confirm that everything comes through. If any of your value expressions can’t be evaluated by Okta, the claim name will not exist in the ID token, so you probably need to double-check your custom AS claim settings back on Okta if things aren’t there.
2 Likes

Doh! Now I’ve got all the data I need in the ID token, but Ignition doesn’t fully expose that to Perspective…

For my next trick: watch me overload one of the mappable fields like “lastName” with all of the properties jammed together in a format that my Perspective scripts will be able to pick apart to get the individual properties back… :frowning:

Some more useful tidbits of information for anyone else following in this path…

  • When setting user attribute mapping rules on an IdP and you switch to “expression” mode (instead of “direct”), the syntax for referencing ID token properties is “{idp-attributes:X}”. I didn’t see this documented anywhere and only stumbled on it by accident.
  • You can use all of the ID token properties in your security level rules, even ones that are never exposed elsewhere!
  • You can as a last-ditch solution pack lots of properties into one. I just tested it by putting all of my leftover properties into a JSON string, and then putting that into the lastName field. The expression mapping looked like this:

jsonSet(jsonSet(jsonSet("{'sf_groups':'', 'sms':'', 'lname':''}", "sf_groups", {idp-attributes:sf_groups}), "sms", {idp-attributes:sms}), "lname", {idp-attributes:lname})

Then in Perspective I have to decode session.props.auth.user.lastName as JSON to get all my other fields back.

3 Likes

This is very useful information, thank you for taking the time to share your struggles. We need some kind of extension mechanism so that you can add custom user properties from a user attribute mapper on the Ignition side. I created feature ticket 16160 to track this.

When setting user attribute mapping rules on an IdP and you switch to “expression” mode (instead of “direct”), the syntax for referencing ID token properties is “{idp-attributes:X}”. I didn’t see this documented anywhere and only stumbled on it by accident.

We do have this documented here for future reference: https://docs.inductiveautomation.com/display/DOC80/Security+Level+Rules#SecurityLevelRules-SpecialObjectReference

The security level documentation is where I stumbled on it. My complaint was that that documentation only says it will work in the context of security level rules, not user attribute mapping. There should be a mention or link to that table from the user attribute mapping manual page: https://docs.inductiveautomation.com/display/DOC80/User+Attribute+Mapping

My complaint was that that documentation only says it will work in the context of security level rules, not user attribute mapping.

I’m not finding where the docs say that. If you could you link me to the page which says that (and which section it is under), I’ll have that fixed right away.

There should be a mention or link to that table from the user attribute mapping manual page

We do have a mention + link in the first paragraph under the section titled “Configuring User Attributes”:

There are two ways of mapping user attributes: Direct and Expression . Direct mappings require that you enter in the path to the attribute in the response document that you want to map to that particular property. Expression mappings allow you to use the expression language to derive the attribute from contextual data such as the IdP response document or tags. The IdP Attributes and Security Zones can be accessed in the same way as the expressions for the Security Level Rules.

Maybe we could add examples of expression attribute mappings on the User Attribute Mapping manual page to make it a little more clear?

1 Like

You’re right. I just missed that several times in a row. I’m sorry.

Yes, that would’ve gotten me past my sticking point handily.

2 Likes

Update: we have delivered the capability to call the user info endpoint and to use the access token. There is also a perspective session prop which points to these objects. See Added Support for OIDC User Info and Token Endpoint Response for more details.