Understanding the Single Sign-On Extension

Related Resources

Overview

The Single Sign-On Extension (SSOE) solves a common problem for employees in larger organizations. An employee commonly uses multiple organization-related apps and webpages throughout the day. Each app may require authentication to access the resources in that app or website. To solve this problem, the SSOE provides a way to handle separate app requests and provide the same credentials to each app. The employee only has to sign-in the first time, and each subsequent login will not prompt for their credentials again. The SSOE provides a secure way to authenticate different apps and websites if they all share the same Identity Provider (IdP) without prompting each time a different app is accessed. The Single Sign On Extension provides a way to securely share credentials between apps by handling the URL requests sent to the identity provider and sharing the authentication between different apps including web browsers such as Safari.

This article is focused on anyone who needs to know more about how the SSOE works, including Identity Providers looking to develop a single sign-on extension, and members of IT looking to deploy a single sign-on extension in their organization.

The goal of this article is to explain how all their different parts of the required infrastructure work together to effectively deploy a single sign-on extension. The SSOE does not provide or require any specific authentication protocols. The SSOE provides the developer a way to implement any required authentication and extended the operating system to allow all apps to use their extension to authenticate.

While the SSOE does not require any specific authentication protocols, it provides the developer a way to implement any required authentication and extends the operating system to allow all apps to use the SSOE extension to authenticate. The key concept to understand about the SSOE is that it extends the authentication protocols to the operating system, not just the app that contains the SSOE. The SSOE acts as a system extension that gets registered on installation. If the extension and the operation system are configured correctly, requests from apps attempting to authenticate with an identity provider will be routed to the SSOE to provide a seamless single sign-on experience.

Required Components And Services

Created Components

The SSOE requires the following components to be created:

  1. Container App: A standard macOS or iOS application that contains a single sign-on extension (SSOE). The app does not have to provide any functionality outside of hosting the SSOE but typically has information or utility functions related to the Identity Provider. If the Identity Provider already has an app that provides functionality, the SSOE can be added to that app. The updated app with the extension can then be provided in an update to the customer. Entitlements are requests to Apple for specific requirements for OS functionality. The only SSOE-specific requirement for the Container App is that it must have an entitlement containing the Identity Provider’s hostname to allow the extension to route requests to the extension. The SSOE Domain is required for the OS to verify the IdP allows the extension to access the IdP.
  2. Single Sign-On Extension: A macOS or iOS system extension that gets registered with the OS when the container app is installed. The SSOE must be configured from an MDM profile.
  3. Mobile Device Management Configuration Profile: A configuration file for the SSOE that must be delivered with an Mobile Device Management (MDM) server. This configuration file associates the authentication URLs with the specific extension by the bundle identifier.
  4. Associated Domain File: An JSON file that is uploaded to a specific endpoint on the Identity Provider’s web service. This file identifies the developer and extension that will be accessing the Identity Provider through the extension. The file is checked by the OS when the SSOE is installed.

Required Services

To develop and deploy a SSOE, you will need access to their services:

  1. Identity Provider (IdP): The web service used for providing user authentication. The IdP must provide the Associated Domain file in a specific location with content matching the Container App’s entitlement.
  2. Mobile Device Management (MDM) Service: Provides the configuration for the SSOE. The MDM Service must provide configuration and cannot be provided by other means for the SSOE to handle requests.

Core Functionality

A SSOE extension is distributed inside a container app, and the SSOE is registered with the OS to route URLs defined in the MDM profile to the SSOE. The app requesting a URL requiring SSO can be any app installed on the OS. A SSOE does not extend the container app functionality; it extends the functionality of the OS.

When any installed app requests authentication with a web service for a URL that requires authentication, the URL request is held and the SSOE extension is passed the URL request and a URL session. The SSOE extension can then complete the request by showing a user interface (typically for entering a username and password) and calling services required to complete the authentication. The SSOE can also securely cache required credentials to process subsequent requests without user interaction. Once authentication is complete, the initial URL request is completed by the SSOE, usually by redirecting the requesting app’s URL request back to the requested service with the provided authentication in the redirected request.

The app itself does not need to be aware of the SSO functionality and does not need any Identity Provider-specific user interface built into the app requesting authentication. The authentication information is shared between any apps that call the defined authentication URLs, including a browser. This provides SSO between the browser session and the app, resulting in the user only entering in their credentials once.

The SSOE is protocol agnostic and does not require the app to know that the SSOE extension is being used. From the app perspective, the app requests a network resource and the content is delivered in the response. The SSOE can authenticate using OIDC redirects, SAML, or other authentication protocols. Once the authentication is complete, the response can populate cookies, set an authentication header, and redirect to another URL. It is up to the IdP to implement the SSOE that best implements the required authentication protocols for authenticating the user and caching the authentication information.

Trust and Requirements

Organization Trust

For the SSOE extension to be deployed successfully, there are trust requirements and protections to limit the scope of what an SSOE can access:

  1. Organization Trust of App: The SSOE must be sandboxed and code-signed by a certificate issued by Apple. The extension is trusted by Apple to run on the device because it is signed by a certificate from Apple or with an organization’s certificate issued by Apple.
  2. Organization Trust of URLs: URLs that are handled by the SSOE must be specified in an MDM profile. An organization that manages the devices enrolled in the MDM can trust that only the URLs specified in the MDM-delivered profile will be handled by the SSOE.
  3. OS trust of IDP: An associated domain file is made available on the the IdP and cached by Apple’s CDN.

Associated Domain Requirement

Any URLs specified in the MDM profile must be hosted on publicly available websites that use TLS certificates issued by well-known certificate authorities trusted in the OS root trust store and that the owner of that website has a relationship with the organization deploying the extension. This trust is enabled through the associate domains file, checked by Apple at a well-known location on the IdP service, and cached on Apple’s Content Delivery Network (CDN).

Setup

For the SSOE to work, the following conditions must be met:

  1. The container app and extension must be signed with a valid developer certificate.
  2. The container app must be hardened and notarized (on macOS).
  3. The extension must be sandboxed (on both macOS and iOS).
  4. The container app must have an Associated Domain entitlement with a hostname defined to a publicly accessible web server. This entitlement is specified in Xcode for the container app.
  5. The publicly accessible web server must use an SSL certificate trusted by iOS or macOS root trust.
  6. The publicly accessible web server must return valid JSON from /.well-known/apple-app-site-association that contains the team ID and bundle ID of the container app in a specific format. This URL endpoint must be the exact path at the root level of the host specified in the entitlement.
  7. An MDM SSOE configuration profile must be installed by an MDM and must contain URLs whose hostname matches the hostname defined in the entitlement and the com.apple.associated-domains MDM profile (macOS or iOS). Alternatively, the com.apple.associated-domains can be distributed by the AssociatedDomains property of the InstallApplicationCommand on iOS.

Associated Domains

Associated Domains establish a secure association between domains and your app so you can share credentials or provide features in your app from your website. In a SSOE, the extension is associated with a website (typically the Identity Provider) with an entitlement of the container app and an apple-app-site-association file at /.well-known/apple-app-site-association on the IdP.

Entitlement and apple-app-site-association

The entitlement must have one or more hostnames in an array with the com.apple.developer.associated-domains key and the authsrv prefix. For example:

<key>com.apple.developer.associated-domains</key>
<array>
    <string>authsrv:idp.example.com</string>
</array>

The apple-app-site-association must be on the root of the server at the hostname specified in the entitlement and located at the path /.well-known/apple-app-site-association. The file must be valid JSON and list the bundle identifier of the container app prefixed by the team id of the developer in an “apps” array under a “authsrv” key. For example, if the developer’s team ID is UXP6YEHSPW and the container app bundle identifier is com.example.Scissors, the JSON file would be:

{
    authsrv =     {
        apps =         (
            "UXP6YEHSPW.com.example.Scissors",
        );
    };
}

When the container app is installed, the entitlement is detected and the OS looks to Apple’s Content Delivery Network (CDN) for the cached version of contents of the file located at the path /.well-known/apple-app-site-association on the specified hostname. The first time the request is sent to the CDN, the CDN will reach out to the host, download the file, cache it and then return the contents of the file. Subsequent requests will only return the cached value, and the CDN will only check for updates periodically (typically 15 days, based on the max-age). To verify the current contents of the CDN for a domain, it can be looked up in a browser by visiting Apple’s CDN website at app-site-association.cdn-apple.com:

https://app-site-association.cdn-apple.com/a/v1/example.com

When developing an SSOE, entitlement can add a parameter to have the OS verify the apple-app-site-association file directly on the host rather than the CDN by adding the “?mode=developer” to the entitlement string. For example:

<key>com.apple.developer.associated-domains</key>
<array>
    <string>authsrv:idp.example.com?mode=developer</string>
</array>

This additional parameter will only be enabled if the device if Universal Links developer mode is enabled. To enable on iOS, go to Settings > Developer, scroll to the Universal Links and enable Associated Domains Development. On macOS, open Terminal and run:

sudo swcutil developer-mode -e true

The swcutil can also be used to look up what associated domains are registered by running:

sudo swcutil show

swcutil can also be used to view the associated domain file for a specific host:

sudo swcutil  dl -d idp.example.com

If the associated domain is not correct on the CDN, does not match the team id and app identifier, the app is signed by a different developer identity or the bundle id does not match, the extension will not be called. Along with using the swcutil commands above to verify, checking the system log when launching app will show the associated domains:

log stream --predicate 'category contains "SOExtensionManager"'

The log will show the bundle ID of the extension followed by the associatedDomains. For example, the following log show that the extension with bundle id com.example.Scissors.ssoe has a verified associated domain at idp.example.com. com.example.Scissors.ssoe => <SOExtension:0x600003f3ca00,bundleID=com.example.Scissors.ssoe, path=ssoe.appex,associatedDomains=(“idp.example.com”)>

If the correct domain is not associated with SSOE bundle ID, the extension will not be called. This must be resolved to enable extension functionality.

Providing additional associated domains

Once the app has been compiled and deployed, the associated domain entitlement and hostnames in the entitlement cannot be changed. However, additional hostnames can be provided. On macOS, additional com.apple.developer.associated-domains can be provided via the com.apple.associated-domains MDM profile. On iOS, additional additional hostnames for Associated Domains can be provided with the InstallApplication Command.

MDM Profile

Once the associated domains have been configured and set up correctly, the MDM Profile for the SSOE provides the URLs that are sent to the SSOE for processing. See Extensible Single Sign-on MDM payload settings for Apple devices from the Apple Platform Deployment Guide for full details.

The ExtensionIdentifier must match the bundle identifier of the SSOE. The payload type must be “PayloadType”. The Team Identifier must match the team identifier of the identity used to sign the extension. The Type for non-kerberos single sign-on extensions will be “Redirect”. The URLs array defines the URLs that will be sent to the SSOE to be processed. These URLs must contain the defined associated domains or those URLs will not be handled by the SSO extension. A sample payload is shown below.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>PayloadContent</key>
    <array>
        <dict>
            <key>ExtensionIdentifier</key>
            <string>com.example.Scissors.ssoe</string>
            <key>PayloadDisplayName</key>
            <string>Single Sign-On Extensions #1</string>
            <key>PayloadIdentifier</key>
            <string>com.apple.extensiblessoD5AC0626-265B-406F-9497-567D8CD58B0A</string>
            <key>PayloadType</key>
            <string>com.apple.extensiblesso</string>
            <key>PayloadUUID</key>
            <string>CDC67F3E-0687-4796-95B0-A61EF6F3F9A7</string>
            <key>PayloadVersion</key>
            <integer>1</integer>
            <key>TeamIdentifier</key>
            <string>UXP6YEHSPW</string>
            <key>Type</key>
            <string>Redirect</string>
            <key>URLs</key>
            <array>
                <string>https://idp.example.com/realms/domain/</string>
            </array>
        </dict>
    </array>
    <key>PayloadDisplayName</key>
    <string>SSOE</string>
    <key>PayloadIdentifier</key>
    <string>mdscentral.CDC67F3E-0687-4796-95B0-A61EF6F3F9A7</string>
    <key>PayloadScope</key>
    <string>System</string>
    <key>PayloadType</key>
    <string>Configuration</string>
    <key>PayloadUUID</key>
    <string>0DC6670F-F853-49CB-91B3-1C5ECB5D3F46</string>
    <key>PayloadVersion</key>
    <integer>1</integer>
</dict>
</plist>

Troubleshooting

If the single sign-on extension is not being called as expected when developing the extension, try these steps to resolve the issue:

  1. Do a clean build by selecting Product->Clean Build Folder and building the container app and extension again. This will re-register the extension with the OS.
  2. On macOS, verify that additional copies of the container app and extension do not exist on the system. These can be identified with pluginkit -v -m. This command will show the path to the location of the installed extension and the bundle identifier.
  3. Make sure the associated domain entitlement is defined on the container app and not the extension.
  4. Verify the entitlement has the correct team identifier and bundle ID by running codesign -dv <path to container app> and comparing to the associated domain JSON file on the server.
  5. Verify the associated domain file is located at https://<hostname>/.well-known/apple-app-site-association and match the bundle identifier and team identifier.
  6. Make sure the associate domain entitlement has the prefix “authsrv:”.
  7. Verify the log shows the correct associated domain with the SSOE by running the log stream --predicate 'category containing the "SOExtensionManager"'command, then cleaning the project in Xcode and then building and running the container app.
  8. Verify that the CDN version of the app site association file contains the correct information by viewing https://app-site-association.cdn-apple.com/a/v1/<HOSTNAME> in a browser.

Developing a Single Sign-In extension or Platform Single Sign-In extension for your organization and need some assistance? Get in touch!