We made some progress but we’re still hitting issues.
So our flow now is :
- User logs in to web application.
- As part of the log in flow, web application redirects to Auth0 to complete the authentication process.
- Auth0 returns a JWT token to web application.
That’s pretty standard so far.
- User triggers a function in Web application that requires data from Snowflake
- Web application has to make another call to Auth0 to generate another JWT token with a single scope value.
- Web application then calls Tyk passing the JWT token as payload.
- Tyk applies the necessary Tyk policy based on the JWT token contents (easy enough to do in Tyk).
- Tyk calls a data access layer we built and passes along the JWT token as payload.
- Data access layer calls Snowflake using that same JWT token inserted into the Snowflake connection object as a member.
The issue is in step #5. There are 2 general methods to call Auth0 a second time for the same user context to generate another JWT token:
- Use a refresh token. This works, tested, but there are rate limits applied by Auth0 for the refresh token endpoint (50 calls per minute with bursts of max 500; see Rate Limit Policy). I can see how we can easily hit this limit if our web application is popular (heck, today, it’s the web application, tomorrow it can be the web application and 3 other mobile apps). So this is just going to hit a scalability wall in the future.
- Use silent authentication in Auth0 (Configure Silent Authentication). But, because we also use MFA to protect our Auth0 identities, it will trigger a mfa_required error when we use this. We also don’t want the user to be prompted by MFA requirements multiple times while using the web application. Hence, we have to configure an Auth0 rule to require MFA ONLY ONCE per user session ( Sessions). This seems like the better approach.
As an ancillary, we also noticed the Snowflake library we use for authentication takes a “role” parameter. The “role” parameter is directly correlated to the values in the scope claim of the JWT token. We now face another issue. If we have a JWT token with 3 scope values, e.g.:
scope:read:app1 write:app2 snowflakerole
And in our connection to Snowflake, we put “snowflakerole” into the “role” parameter, Snowflake still throws an error saying the role does not exist in the token. We suspect the problem is due to the way the “scope” claim is formatted. We do know that if we specify role = “snowflakerole” , and the token has “scope:snowflakerole” ONLY, the connection succeeds.
Auth0 formats the scope claim as a space-separated list. Snowflake, we suspect, can only accept an array (this is a suspicion at this point, so can’t blame Snowflake yet lol).
Furthermore, RFC6749 para 3.3, RFC 8693 para 4.2, and RFC7662 para 2.2 all state that it should be space-separated. However, we have seen in the wild some systems using an array for multiple scope values.
We have opened a support ticket with Snowflake to clarify this question. I will see if there is a way we can test this theory (still thinking because it requires Auth0 to forcibly generate multiple scope claims as an array).
While we have a working solution, it would be nice if we can get Snowflake to do this because it means our web application no longer needs to deal with multiple Auth0 calls:
- I pass a role parameter to Snowflake to specify what role I require when I am connecting.
- I pass a JWT token with multiple values in the scope claim. One of the values corresponds to #1.
- Snowflake parses the scope claim and correctly finds the role being requested and allows the connection.
By the way, these multiple calls to Auth0 must be done with custom code - it’s not something supported by the Auth0 libraries in the wild as far as we can tell. The libraries do support automatically refreshing expired tokens but this happens in the background and its not publicly exposed as a method we can call while specifying a different scope claim. The automatic refresh gets you the same JWT token every time. So it’s more work we are trying to avoid if we can - prefer to re-use libraries than to write custom code.
One other possible solution is for us use Tyk as an OAuth provider. This means all backend systems that are OAuth compliant will be integrated with Tyk for the OAuth flow. However, we will still keep Auth0 as our IdP (so some functions like SCIM integration, SSO, etc will still be kept in Auth0), but only outsource the OAuth negotiation specifically to Tyk. I don’t know if Tyk can do this? I do know that some well known OAuth endpoints don’t exist in the Tyk gateway (I tested and I only see “NOT FOUND” appearing in my browser - so i think Tyk is treating these endpoints as published API endpoints that Tyk is proxying).
We also thought of whether it’s possible for Tyk to strip away values from the JWT scope claim and only keep the relevant one for Snowflake but this is going to fundamentally break the whole reason why the JWT token is signed in the first place - so that it is not modified. I suppose the suggestion from sedky would be the way to go - a custom plugin. A shared key implies HMAC signing algorithm - I don’t believe Snowflake supports this. That means I need to maintain a separate set of private/public keys - which comes with its own operational overhead (e.g. key rotation!).