Application multi-tenancy is supported from the SAS Viya LTS 2021.2 release. In this article we will look at configuring OpenID Connect (OIDC) authentication in a multi-tenant (M-T) environment. We will examine some general concepts and dive into the details of how the authentication mechanism can be configured. For the discussions our SAS Viya environment will have the M-T provider and two tenants: a marketing tenant and a sales tenant.
The SAS Viya configuration of OIDC authentication could be performed in the M-T provider and apply to all tenants or could be configured in one or more individual tenants and only apply to that tenant. For our discussions here we will configure ODIC for the M-T provider and the marketing tenant.
The third-party OIDC Identity Provider, such as Azure Active Directory, has three key properties. These are:
So, if we configure our M-T provider for OIDC and expect the M-T provider, marketing tenant, and sales tenant to be able to authenticate we must register three Redirect URIs. If the name of the configuration in SAS Viya is oidc_azure, these Redirect URIs would be:
With SAS Viya 2021.2 (and later) to be able to specify the specific tenant which we want to apply the configuration to, we must use the SAS Viya CLI. With the SAS Viya CLI you can create a separate profile for the provider and each tenant. For example, to create a profile for the marketing tenant you would use the following:
INGRESS_URL=https://marketing.{{INGRESS_NAME}} ;\
/opt/sas/viya/home/bin/sas-viya --profile marketing profile set-endpoint "${INGRESS_URL}"
/opt/sas/viya/home/bin/sas-viya --profile marketing profile toggle-color true
/opt/sas/viya/home/bin/sas-viya --profile marketing profile set-output fulljson
In the examples that follow the default SAS Viya CLI profile will target the M-T provider, and the two tenants marketing, and sales, will have their own profiles.
For the example configuration provided in this article we will use RedHat Keycloak as the third-party OpenID Connect Identity Provider. In our case Keycloak has been configured with two OIDC client registrations. The first with the Client ID = OIDC_Viya has all three valid Redirect URIs registered, which means that Keycloak will be able to authenticate end-users from the M-T provider or any tenants using this client. The second with the Client ID = OIDC_Marketing only has the Redirect URI registered for the marketing tenant, so only end-users from the marketing tenant can be authenticated with this client.
To configure the SAS Viya multi-tenant provider, we will create a JSON configuration file which we will then apply with the SAS Viya CLI. For example, we can create the JSON configuration file with the following:
mkdir -p $deploy/site-config/JSON
tee $deploy/site-config/JSON/OIDC_Provider.json > /dev/null << EOF
{
"name": "addOIDC",
"items": [
{
"version": 1,
"metadata": {
"allowUserProperties": true,
"services": ["SASLogon","credentials"],
"isDefault": false,
"tenant": "provider",
"mediaType": "application/vnd.sas.configuration.config.sas.logon.oauth.providers+json;version=1"
},
"addShadowUserOnLogin": true,
"attributeMappings.user_name": "preferred_username",
"authUrl": "https://gellabdc1.gellab.net/auth/realms/GELRealm/protocol/openid-connect/auth",
"clientAuthInBody": false,
"discoveryUrl": "https://gellabdc1.gellab.net/auth/realms/GELRealm/.well-known/openid-configuration",
"emailDomain": "gellab.net",
"linkText": "Provider OIDC",
"name": "oidc_provider",
"relyingPartyId": "OIDC_Viya",
"relyingPartySecret": "{{clientSecret}}",
"scopes": "openid",
"showLinkText": true,
"tokenUrl": "https://gellabdc1.gellab.net/auth/realms/GELRealm/protocol/openid-connect/token"
}
]
}
EOF
Notice that in the metadata block of the JSON we have the property tenant: provider. This will define the configuration as being for the specified tenant. Also, the name property needs to be unique across all environments, so the provider and tenants. If the name is not unique, we will have unexpected behaviour, which we will discuss later.
This configuration can then be simply applied using the SAS Viya CLI:
/opt/sas/viya/home/bin/sas-viya auth login -u sasboot
/opt/sas/viya/home/bin/sas-viya configuration configurations create \
--file $deploy/site-config/JSON/OIDC_Provider.json
/opt/sas/viya/home/bin/sas-viya auth logout
Which will prompt for the sasboot user’s password.
If we review the logs for SAS Logon Manager after applying this configuration, we will see:
INFO 2022-01-05 06:27:47.894 +0000 [sas-logon-app] - Refresh keys changed: [tenants.provider.sas.logon.oauth.providers.oidc_provider.tokenUrl, sas.logon.oauth.providers.oidc_provider.tokenUrl,
sas.logon.oauth.providers.oidc_provider.authUrl, sas.logon.oauth.providers.oidc_provider.showLinkText, tenants.provider.sas.logon.oauth.providers.oidc_provider.showLinkText,
sas.logon.oauth.providers.oidc_provider.clientAuthInBody, sas.logon.oauth.providers.oidc_provider.linkText, sas.logon.oauth.providers.oidc_provider.attributeMappings.user_name,
tenants.provider.sas.logon.oauth.providers.oidc_provider.relyingPartyId, tenants.provider.sas.logon.oauth.providers.oidc_provider.scopes, tenants.provider.sas.logon.oauth.providers.oidc_provider.addShadowUserOnLogin,
tenants.provider.sas.logon.oauth.providers.oidc_provider.clientAuthInBody, tenants.provider.sas.logon.oauth.providers.oidc_provider.authUrl, sas.logon.oauth.providers.oidc_provider.scopes,
tenants.provider.sas.logon.oauth.providers.oidc_provider.attributeMappings.user_name, tenants.provider.sas.logon.oauth.providers.oidc_provider.emailDomain, tenants.provider.sas.logon.oauth.providers.oidc_provider.discoveryUrl,
sas.logon.oauth.providers.oidc_provider.addShadowUserOnLogin, tenants.provider.sas.logon.oauth.providers.oidc_provider.responseType, tenants.provider.sas.logon.oauth.providers.oidc_provider.relyingPartySecret,
sas.logon.oauth.providers.oidc_provider.relyingPartyId, sas.logon.oauth.providers.oidc_provider.responseType, tenants.provider.sas.logon.oauth.providers.oidc_provider.linkText,
sas.logon.oauth.providers.oidc_provider.discoveryUrl, sas.logon.oauth.providers.oidc_provider.emailDomain, sas.logon.oauth.providers.oidc_provider.relyingPartySecret]
This shows that the configuration properties we have set using the SAS Viya CLI have been picked up by SAS Logon Manager. Notice that although we specified "tenant": "provider" when SAS Logon Manager reads the M-T provider settings it also makes those available as service wide properties. Each property is prefixed by tenants.provider.sas.logon.oauth.providers.oidc_provider and sas.logon.oauth.providers.oidc_provider. This means that the settings will apply to the M-T provider and all tenants.
However, at this stage the link to authenticate with OIDC will only be displayed on the SAS Logon Manager page for the M-T provider. Only after restarting SAS Logon Manager will the link appear on the pages for the marketing and sales tenants.
Next, we will create the ODIC configuration for the marketing tenant. For example, we can create the JSON configuration file with the following:
mkdir -p $deploy/site-config/JSON
tee $deploy/site-config/JSON/OIDC_Marketing.json > /dev/null << EOF
{
"name": "addOIDC",
"items": [
{
"version": 1,
"metadata": {
"allowUserProperties": true,
"services": ["SASLogon","credentials"],
"isDefault": false,
"tenant": "marketing",
"mediaType": "application/vnd.sas.configuration.config.sas.logon.oauth.providers+json;version=1"
},
"addShadowUserOnLogin": true,
"attributeMappings.user_name": "preferred_username",
"authUrl": "https://gellabdc1.gellab.net/auth/realms/GELRealm/protocol/openid-connect/auth",
"clientAuthInBody": false,
"discoveryUrl": "https://gellabdc1.gellab.net/auth/realms/GELRealm/.well-known/openid-configuration",
"emailDomain": "gellab.net",
"linkText": "Marketing OIDC",
"name": "oidc_marketing",
"relyingPartyId": "OIDC_Marketing",
"relyingPartySecret": "{{clientSecret}}",
"scopes": "openid",
"showLinkText": true,
"tokenUrl": "https://gellabdc1.gellab.net/auth/realms/GELRealm/protocol/openid-connect/token"
}
]
}
EOF
Notice that we target the marketing tenant since the metadata sections contains "tenant": "marketing". Also notice that the name and relyingPartyId are oidc_marketing and OIDC_Marketing respectively. Remember we must have the name property as a unique value for the provider and all tenants.
Now when we use the SAS Viya CLI to load this configuration we must leverage the marketing profile we setup earlier. Also, we must authenticate as the sasprovider user and not sasboot.
/opt/sas/viya/home/bin/sas-viya --profile marketing auth login -u sasprovider
/opt/sas/viya/home/bin/sas-viya --profile marketing configuration configurations create \
--file $deploy/site-config/JSON/OIDC_Marketing.json
/opt/sas/viya/home/bin/sas-viya --profile marketing auth logout
Again, reviewing the log for SAS Logon Manager, you will see the configuration apply with the following:
INFO 2022-01-05 06:43:56.254 +0000 [sas-logon-app] - Refresh keys changed: [tenants.marketing.sas.logon.oauth.providers.oidc_marketing.emailDomain, tenants.marketing.sas.logon.oauth.providers.oidc_marketing.discoveryUrl,
tenants.marketing.sas.logon.oauth.providers.oidc_marketing.attributeMappings.user_name, tenants.marketing.sas.logon.oauth.providers.oidc_marketing.scopes,
tenants.marketing.sas.logon.oauth.providers.oidc_marketing.relyingPartySecret, tenants.marketing.sas.logon.oauth.providers.oidc_marketing.clientAuthInBody,
tenants.marketing.sas.logon.oauth.providers.oidc_marketing.responseType, tenants.marketing.sas.logon.oauth.providers.oidc_marketing.relyingPartyId, tenants.marketing.sas.logon.oauth.providers.oidc_marketing.tokenUrl,
tenants.marketing.sas.logon.oauth.providers.oidc_marketing.addShadowUserOnLogin, tenants.marketing.sas.logon.oauth.providers.oidc_marketing.linkText, tenants.marketing.sas.logon.oauth.providers.oidc_marketing.authUrl,
tenants.marketing.sas.logon.oauth.providers.oidc_marketing.showLinkText]
So, in this case, the configuration is applied only to the marketing tenant. Notice that each property is only prefaced with tenants.marketing.sas.logon.oauth.providers.oidc_marketing.
With these two sets of configurations loaded into the SAS Viya multi-tenant environment we will have:
Select any image to see a larger version.
Mobile users: To view the images, select the "Full" version at the bottom of the page.
One way to prevent the marketing tenant from displaying the M-T provider OIDC link is to ensure the configuration within the marketing tenant has the same name as the configuration within the M-T provider. Which is why we stated earlier the name should be unique. This means in the OIDC_Marketing.json file changing the value "name": "oidc_marketing" to "name": "oidc_provider" to match the value we used in the OIDC_Provider.json file. To do this you will need to first remove the OIDC configuration within the marketing tenant.
We need to remove the existing oidc_marketing configuration from the marketing tenant. This involves both removing the configuration using the SAS Viya CLI and cleaning up one of the database tables used by SAS Logon Manager. You could attempt to clean-up the database table directly; a better approach is to use the SAS Logon Manager API to perform this clean-up.
In a future release we expect that removing the configuration with the SAS Viya CLI will be sufficient and no other clean-up will be required.
You can use the following commands to delete the existing OIDC configuration from the marketing tenant using the SAS Viya CLI:
/opt/sas/viya/home/bin/sas-viya --profile marketing auth login -u sasprovider
my_id=`/opt/sas/viya/home/bin/sas-viya --profile marketing configuration configurations list |grep -A 1 sas.logon.oauth| awk -F\" '{print $4}'|tail -1`
/opt/sas/viya/home/bin/sas-viya --profile marketing configuration configurations delete --id ${my_id}
/opt/sas/viya/home/bin/sas-viya --profile marketing auth logout
Then use the following commands to use the SAS Logon Manager API to remove the oidc_marketing internal reference:
/opt/sas/viya/home/bin/sas-viya auth login -u sasboot
myToken=`cat ~/.sas/credentials.json |jq -r '.Default."access-token"'`
myTenant="marketing"
myHost={{INGRESS_NAME}}
myID=`curl https://${myHost}/SASLogon/identity-providers \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${myToken}" \
-H "X-Identity-Zone-Id: ${myTenant}" | jq -r '.[] | select(.originKey == "oidc_marketing ").id'`
curl -X DELETE https://${myHost}/SASLogon/identity-providers/${myID} \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${myToken}" \
-H "X-Identity-Zone-Id: ${myTenant}"
/opt/sas/viya/home/bin/sas-viya auth logout
Notice that we are using the SAS Viya CLI to authenticate to the M-T provider as the sasboot user. We then use the internal token that this authentication provides to call the SAS Logon Manager API. But when we call the /identity-providers API we include the X-Identity-Zone-Id header that identifies the marketing tenant. First, we use the /identity-providers API to find the ID of the oidc_marketing definition. Then we use the DELETE method of the /identity-providers API to remove that specific ID.
With these two steps complete, SAS Logon Manager should be restarted. Once SAS Logon Manager has been restarted the OIDC configuration can be loaded for the marketing tenant using the SAS Viya CLI. However, this time the name of the configuration will be oidc_provider.
Since the name of the configuration loaded to the marketing tenant matches the name loaded to the M-T provider the values from the marketing tenant will supersede those from the M-T provider. This means that the marketing tenant SAS Logon Manager login form will now only display a single link.
Our desired configuration of the SAS Viya multi-tenant environment is almost complete. The M-T provider only uses the M-T provider configuration, and the marketing tenant now only uses the marketing tenant configuration. However, the sales tenant is still using the M-T provider configuration. This might be a desired outcome if we want to share the OIDC configuration between the M-T provider and the sales tenant.
Instead, let’s look at how we can prevent the sales tenant using the M-T provider OIDC configuration. This will leave the sales tenant displaying the standard login form with no additional authentication options. Again, we can leverage the SAS Logon Manager API. We will use the SAS Logon Manager API to set the propagated M-T provider configuration as not active in the sales tenant.
Use the following commands to update the propagated configuration in the sales tenant using the SAS Logon Manager API:
/opt/sas/viya/home/bin/sas-viya auth login -u sasboot
myToken=`cat ~/.sas/credentials.json |jq -r '.Default."access-token"'`
myTenant="sales"
myHost={{INGRESS_NAME}}
myID=`curl https://${myHost}/SASLogon/identity-providers \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${myToken}" \
-H "X-Identity-Zone-Id: ${myTenant}" | jq -r '.[] | select(.originKey == "oidc_provider").id'`
myConf=`curl https://${myHost}/SASLogon/identity-providers/${myID} \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${myToken}" \
-H "X-Identity-Zone-Id: ${myTenant}"`;\
myConf=`echo $myConf|sed 's/"active":true/"active":false/'`
curl -X PUT https://${myHost}/SASLogon/identity-providers/${myID} \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${myToken}" \
-H "X-Identity-Zone-Id: ${myTenant}" \
-d "${myConf}"
/opt/sas/viya/home/bin/sas-viya auth logout
As before, we authenticate using the SAS Viya CLI as sasboot in the M-T provider. The header X-Identity-Zone-Id then identifies which tenant we are operating in.
These commands are using the SAS Logon Manager API to find the ID for the OIDC configuration internally in SAS Logon Manager for the sales tenant. Next we fetch that configuration and update the value "active":true to "active":false. Finally, the updated configuration is uploaded using the SAS Logon Manager API. Setting "active":false within the sales tenant will prevent the configuration being used.
Our SAS Viya multi-tenant environment is now showing what we expect on the SAS Logon Manager login form for the M-T provider and the two tenants. The M-T provider page has a link to use the M-T provider configured OIDC, the marketing tenant also has a link but to use the marketing configured OIDC, and the sales tenant does not display any additional links.
But what if we want our end-users of the marketing tenant to be automatically redirected to the third-party OIDC provider. So, we don’t want to display the SAS Logon Manager login page for just the marketing tenant. Obviously, the end-users accessing the sales tenant must be able to still access the SAS Logon Manager login page as they have no other way to authenticate.
SAS already provides a sample transformer that enables automatic redirect of sign-ins to the configured third-party OIDC provider. This can be found in your deployment assets in the directory $deploy/sas-bases/examples/sas-logon-app/. We will just change the provided sample slightly to target the marketing tenant.
You can either copy the sample and edit it or use the following to create the YAML file with the required content:
tee $deploy/site-config/sas-logon-app/marketing-login-hint-transformer.yaml > /dev/null << 'EOF'
---
apiVersion: builtin
kind: PatchTransformer
metadata:
name: sas-ingress-login-hint-transformer
patch: |-
kind: Ingress
metadata:
name: sas-logon-app
annotations:
nginx.ingress.kubernetes.io/server-snippet: |
large_client_header_buffers 4 32k;
if ( $args !~ "login_hint" ) { set $test A; }
if ( $uri = "/SASLogon/oauth/authorize" ) { set $test "${test}B"; }
if ( $request_method = "GET" ) { set $test "${test}C"; }
if ( $host ~ "marketing." ) { set $test "${test}D"; }
if ($test = ABCD ) { set $args $args&login_hint=gellab.net; rewrite $uri uri?$args; }
target:
kind: Ingress
name: sas-logon-app
EOF
These annotations that are added to the Ingress rule for SAS Logon Manager do the following:
This means if the GET request to `/SASLogon/oauth/authorize` on the marketing tenant does not have a `login_hint` it will be added. While if the request is not a GET, or already has a `login_hint`, or is not to `/SASLogon/oauth/authorize` on the marketing tenant nothing will happen. Remember the login_hint value is just used as a string match against the text in the emailDomain property and does not have to be an actual email address domain.
Next you need to add the reference to the file site-config/sas-logon-app/marketing-login-hint-transformer.yaml to your kustomization.yaml in the transformers section. Then follow the documented commands to rebuild your site.yaml and apply the updated site.yaml.
Once this is complete there will be no change in behaviour for the M-T provider or the sales tenant. However, for the marketing tenant when end-users access a web application like SAS Drive, they will no-longer see SAS Logon Manager. If the end-user already has a session with the third-party OIDC Identity Provider, they will just be logged into SAS Drive. Alternatively, if they don’t have a current session with the third-party OIDC Identity Provider they will be presented with the OIDC Identity Provider’s login form.
In this article we’ve shown the details of how OIDC configuration is applied to the M-T provider and individual tenants in SAS Viya 2021.2 (and later). Through the examples we’ve shown how the M-T provider configuration is propagated to the tenants. We have shown how this might be a target architecture that you want to have, but also provided a couple of ways to deal with this propagation if you want different configuration in the tenants.
Find more articles from SAS Global Enablement and Learning here.
Join us for SAS Innovate 2025, our biggest and most exciting global event of the year, in Orlando, FL, from May 6-9. Sign up by March 14 for just $795.
Data Literacy is for all, even absolute beginners. Jump on board with this free e-learning and boost your career prospects.