Single sign-on server

The Flowpack.SingleSignOn.Server package provides the components for implementing a custom single sign-on server. This package should be installed in a Flow application that implements custom domain logic and authentication configuration in a project specific package.

Requirements for a single sign-on server:

  • Implementation of a Party domain model and domain logic
  • Configuration of authentication
  • Inclusion of routes for HTTP services
  • Establish a secure server-side communication between the server and instances (e.g. SSL with certificates)
  • Party and account management (optional)
  • Basic user interface for login and display of messages (optional)

Components

This is a schematic view of the single sign-on server components:

The server in detail
Server key pair
The server has a public / private key pair for encryption and verification of requests. The public key is shared with all instances that should access the server.
Service base URI
The server exports HTTP services on a specific URL path. This path acts as the Service base URI (e.g. http://ssoserver.local/sso/) or server identifier.
Clients
All the instances have to be registered as a single sign-on client with their public key and service base URI. This allows for (signed) server-side requests initiated by the client or the server. The client public key restricts access to the single sign-on only to explicitly registered clients. The clients are persisted as entities inside a configured database. A management interface for the clients can be implemented in a custom package.

Note

The server uses the default Flow security framework for authentication during single sign-on requests. So a user that doesn’t have an authenticated session on the server will be delegated to one of the configured authentication providers.

TODO Show usage of authentication provider and accounts on server

Configuration

Package configuration

The Flowpack.SingleSignOn.Server package provides the following default configuration:

Flowpack:
  SingleSignOn:
    Server:
      server:
        # The service base URI for this server
        serviceBaseUri: ''
        # Mandatory key pair uuid (fingerprint) for the SSO server
        keyPairFingerprint: ''
      log:
        backend: TYPO3\Flow\Log\Backend\FileBackend
        backendOptions:
          logFileURL: %FLOW_PATH_DATA%Logs/SingleSignOn_Server.log
          createParentDirectories: TRUE
          severityThreshold: %LOG_WARN%
          maximumLogFileSize: 10485760
          logFilesToKeep: 1
          logMessageOrigin: FALSE

        # Enable logging of request signing (all signed requests)
        logRequestSigning: FALSE
Option Description Mandatory Type Default
server.serviceBaseUri The service base URI for this server Yes string  
server.keyPairFingerprint Key pair fingerprint for the server Yes string  
log.backend Log backend type for the single sign-on logger No string FileBackend
log.backendOptions Log backend options for the single sign-on logger No array see Settings
log.logRequestSigning Controls logging of signed requests via an aspect (for debugging) No boolean FALSE
accountMapper.configuration Serialization of account data for client account mapping in the default SimpleClientAccountMapper. No array NULL

Note

The package also configures some settings for TYPO3 Flow. For the signed requests a security firewall filter with the name ssoServerSignedRequests is configured. This filter can be modified or removed in another package configuration or global configuration.

Caches

A special cache with the identifier Flowpack_SingleSignOn_Server_AccessToken_Storage is used for the storage of Access tokens. It defaults to a FileBackend as the cache backend.

Routes

The routes of the server package have to be registered in the global Routes.yaml:

##
# Flowpack.SingleSignOn.Server subroutes
#

-
  name: 'SingleSignOn'
  uriPattern: 'sso/<SingleSignOnSubroutes>'
  subRoutes:
    SingleSignOnSubroutes:
      package: Flowpack.SingleSignOn.Server

The path sso/ can be freely chosen but will be part of the server service base URI that needs to be configured in the client configuration on a single sign-on instance.

Commands

ssoserver:registerclient

The server exposes a ssoserver:registerclient command for client registration from the CLI:

Add a client

COMMAND:
  flowpack.singlesignon.server:ssoserver:registerclient

USAGE:
  ./flow ssoserver:registerclient <base uri> <public key>

ARGUMENTS:
  --base-uri           The client base URI as the client identifier
  --public-key         The public key fingerprint (has to be imported using the
                       RSA wallet service first)

DESCRIPTION:
  This command registers the specified client at the SSO server.

The key pair has to be created on the instance using the ssokey:generatekeypair command (TODO reference section in the client doc).

Example:

$ ./flow ssoserver:registerclient --base-uri http://ssoinstance.local/sso/ --public-key c1285a470f0fc8f14f54851c5d8eb32f

ssoserver:removeexpiredaccesstokens

The ssoserver:removeexpiredaccesstokens command cleans up expired access tokens from the underlying cache backend.

Remove expired access tokens

COMMAND:
  flowpack.singlesignon.server:ssoserver:removeexpiredaccesstokens

USAGE:
  ./flow ssoserver:removeexpiredaccesstokens

DESCRIPTION:
  This will remove all expired access tokens that were not redeemed from the underlying storage.
  This command should be executed in regular intervals for cleanup.

This command should be executed in regular intervals (e.g. using a cron task) to clean up the access token storage.

Logging

The server package Configuration configures a default logger that is used in the single sign-on package for logging various events. The default file for the logger in Production context is Data/Logs/SingleSignOn_Server.log.

Tip

The log should always be consulted if problems with the single sign-on need to be investigated. In the log level INFO it provides an overview of all single sign-on authentications.

Warning

Access to the logs must be restricted in production since it contains sensible information (session identifiers and access tokens).

The logger interface can be injected in any package to log into the same destination:

/**
 * @var \Flowpack\SingleSignOn\Server\Log\SsoLoggerInterface
 * @Flow\Inject
 */
protected $ssoLogger;

Client registration

Clients of single sign-on instances need to be registered on the server before they can participate in the single sign-on. The server needs the client service base URI and public key for encryption of request parameters, server-side client notification and request signing.

The server package implements the ssoserver:registerclient command for that purpose.

Programmatic client registration

For deployments with many single sign-on instance and respective clients a programmatic registration could be wanted. Since clients are represented by persisted entities this is as easy as creating a new entity and adding it to the repository.

$ssoClient = new \Flowpack\SingleSignOn\Server\Domain\Model\SsoClient();
$ssoClient->setServiceBaseUri($baseUri);
$ssoClient->setPublicKey($publicKeyFingerprint);
$this->ssoClientRepository->add($ssoClient);

It should be trivial to build a management interface for client management.

Tip

Programmatic access to the RSA wallet for key management is easy with an instance of TYPO3FlowSecurityCryptographyRsaWalletServiceInterface.

Authentication endpoint

The single sign-on server exposes a public controller action for handling authentication requests from clients. This is called the authentication endpoint.

The server authentication endpoint

The single sign-on client entry point on the instance will redirect a user to the authentication endpoint on the server if no authenticated session is present locally on the client. This redirect happens in the browser of the user to access an existing web session of the server (if the user authenticated on the server or another instance before).

To secure the parameters and guard against a possible redirection attack the parameters are signed by the client in the signature query argument. This is done using the private key of the client, so the server can verify the signature with the client public key and also verify the identity of the request.

A ClientNotFoundException will be thrown if the client was not registered on the server.

The authentication endpoint action will act as an authentication controller and eventually call $this->authenticationManager->authenticate(), so the user has to authenticate using a configured authentication provider to proceed. This approach integrates nicely with the Flow security framework and allows the usage of all available authentication providers and entry points.

Note

There has to be an entry point (e.g. WebRedirect) that matches the authentication endpoint request. Otherwise a user would not have a chance to authenticate using a login form (or other means depending on the authentication provider). See About the demo server for an example configuration.

Authentication callback

After the user was authenticated on the server or if the user was already authenticated an access token is created on the server and sent to the instance via an redirect in the browser to the client authentication callback.

The client authentication callback

Since the redirect to the client should be considered insecure the access token is encrypted and signed with the client public key and server private key.

Access tokens

The access token stores the account that was authenticated on the server and the client that initiated the authentication request. An access token is identified by a string of 32 random characters. An access token has an expiry setting which defaults to 60 seconds. This and the fact that an access token is deleted after redemption should prevent replay attacks.

Access tokens are stored in a cache backend Flowpack_SingleSignOn_Server_AccessToken_Storage. The default configuration uses a FileBackend. The cache backend allows for a flexible and lightweight storage of access tokens with automatic expiration and garbage collection.

The server package provides a ssoserver:removeexpiredaccesstokens command for the maintenance of the cache backend that will remove expired access tokens that were not redeemed. This command should be executed in regular intervals for garbage collection of the cache backend.

Access token redemption

After the single sign-on client has verified the access token a server-side `signed request`_ is made to exchange the access token for the actual account data and single sign-on session identifier. This measure also prevents injection of arbitrary account data into the callback URI by breaking the signature.

Redeem an access token

With a valid access token the server will:

  • get the original session identifier and account from the access token
  • invalidate (remove) the access token
  • register the single sign-on client in the session for Client notification
  • perform Account mapping to transform the server account into authentication and authorization information for the client
  • respond with a JSON representation of the mapped account and the server session identifier

The client will transform the returned account data into a local account (persistent or transient) using a global account mapper and authenticate this account locally.

Note

The redeem access token request is not public and is guarded by a signed request filter by default. Additional measures to secure this channel should be installed in production environments.

Account mapping

Since the account and party information that is needed on an instance is dependent on the actual requirements of an application the single sign-on solution does not impose a fixed schema for the information.

With an implementation of the ClientAccountMapperInterface any strategy for a transformation given the account and client instance can be implemented:

 interface ClientAccountMapperInterface {

     /**
      * Map the given account as account data for an instance
      *
      * @param \Flowpack\SingleSignOn\Server\Domain\Model\SsoClient $ssoClient
      * @param \TYPO3\Flow\Security\Account $account
      * @return array
      */
     public function getAccountData(
             \Flowpack\SingleSignOn\Server\Domain\Model\SsoClient $ssoClient,
             \TYPO3\Flow\Security\Account $account
     );

}

The getAccountData method has to return the serialized account information based on the given single sign-on client and account object. It is possible to differentiate between clients and return different account information depending on the client with this approach.

SimpleClientAccountMapper

A basic implementation of a client account mapper is included in the server package with the SimpleClientAccountMapper class and will be used by default.

Example account data:

accountIdentifier: 'jdoe'
roles: ['Vendor.MyPackage:User']
party:
  __type: 'Vendor\MyPackage\ExampleParty'
  company: 'Acme Inc.'

The accountIdentifier and roles keys are always returned and do not need any configuration.

The implementation will serialize the party properties according to the configuration property which is configurable via the Flowpack.SingleSignOn.Server.accountMapper.configuration setting.

The default configuration will handle the party type Person and returns all simple properties including the name:

array(
    'party' => array(
            '_exposeType' => TRUE,
            '_descend' => array('name' => array())
    )
);

For any other party implementation it will just return accessible properties directly under the party object, so for relational party data a custom configuration has to be given.

It is important that the type of the party is exposed as the key __type for the default implementation of the `global account mapper`_ on the client (class SimpleGlobalAccountMapper).

Note

The exchange of account data is deliberately unconstrained to allow for a fully flexible exchange of data. But the implementation of the ClientAccountMapperInterface on the server and GlobalAccountMapperInterface on the client have to match in terms of the exported and expecteded properties.

Client notification

The client notification is used to destroy sessions remotely by a server-side request to the client. This is mainly used for synchronized logout (Single sign-off) and account switching on the server.

The server declares a SsoClientNotifierInterface interface for this purpose and provides two implementations using a synchronous (SimpleSsoClientNotifier) and parallel (ParallelSsoClientNotifier) strategy for the HTTP requests. The SimpleSsoClientNotifier is the default implementation configured in the server package Objects.yaml. In scenarios that register a lot of instances for one session the ParallelSsoClientNotifier can reduce the latency on logout or account switching by using parallel HTTP requests with a multi-threading engine.

A destroyed session on the client will require authentication through the single sign-on mechanism on the next request to a secured resource on the client. This ensures an updated authentication state on the instance.

Note

The client notification will destroy all session data on the client. If the instance stores important data in the session this data will be lost on logout or account switching on another instance or the server.

Session synchronization

The TYPO3 Flow session has a configurable interval for inactivity that is used to expire sessions after a certain time of inactivity on the next access or through garbage collection.

In a single sign-on scenario we have to consider multiple Flow sessions (after authentication with at least one instance):

  • One global session on the single sign-on server
  • One ore more local sessions on the instances

The server and instances could have different inactivity timeouts configured for the Flow session which leads to an effect where the user is still authenticated on the client but the server session is already expired due to inactivity (for most scenarios the user will access the server very infrequently). It is desirable that the session lifetime is synchronized in a single sign-on setup, such that an expired session on the server will also expire the session on the instances.

The Flowpack single sign-on solution does use a regular touch on the global session from the client through a special server-side signed request. The interval and frequency is configurable for the single sign-on client.

The server will respond with an error code SessionNotFound if the session was not found / inactive and the client will mark the authentication token as no longer authenticated.

Account impersonation

The authentication endpoint gets the current account that should be passed to the instance through the AccountManager service, which is implemented in the server package.

The method impersonateAccount allows to impersonate another account that will be visible as the globally authenticated account. The original account is still authenticated on the server which allows to switch back to the original or yet another account. As in the case of re-authentication on the server all registered client sessions are destroyed on impersonation.

This feature could be used to implement multi-tenant applications where one global account is able to use multiple other accounts and the user should be able to select the currently active account.

Note

A single sign-on server UI should always use the methods in AccountManager to get the currently active account (through getServerAccount or getImpersonatedAccount) to display authentication information.

HTTP services

This is a list of all HTTP services (controller actions) that are exposed by the server. The URI path depends on the global Routes.yaml that mounts the package subroutes, we expect the routes to be mounted at /sso/<SingleSignOnSubroutes>.

Public

/sso/authentication
Route for the authentication endpoint, has to be accessible for all users that should authenticate using the single sign-on.

Private

The controller for these routes are protected by a signed request firewall filter and should only be accessible by instances. We strongly suggest to take additional measures for securing the server-side channel between the server and instances (e.g. SSL with client certificates, firewall rules, additional request filter).

Warning

The default TYPO3 Flow routes could allow access to controller actions even though the URI paths are secured by a firewall or webserver configuration.

/sso/token/{accessToken}/redeem
Route for the access token redemption, is used by the single sign-on client to verify the access token and to exchange it for account data and the global session identifier.
/sso/session/{sessionId}/touch
Route for the session synchronization by allowing a client to touch the global session in regular intervals and get feedback about the session status.
/sso/session/{sessionId}/destroy
Route for the single sign-off to destroy the global session when a user logs out on an instance.