SRGSSR / srgidentity-apple

The SRG signup and login framework
MIT License
1 stars 0 forks source link

OpenID Connect / Azure B2C PoC implementation #7

Open defagos opened 4 years ago

defagos commented 4 years ago

We may change our existing login provider. This issue discusses some finds and ideas for the future, provided we continue in this direction.

defagos commented 4 years ago

The following documents my findings and recommendations if we want to move forward with this approach.

Client SDK

I used the AppAuth official iOS SDK, which can be integrated with Carthage and builds for iOS and tvOS 9+. There is of course no web browser integration for tvOS, but all necessary APIs are available to implement an authentication flow (see below).

The SDK provides the following features:

The SDK itself is well written, has ample documentation, unit tests and good working examples which can be configured in no time to better understand what happens under the hood. I strongly recommend to use it, though some additional work is required on our side. This work can be mitigated by delivering an updated SRG Identity framework based on AppAuth.

An Android version of the framework is of course available as well.

Background documentation and information

The solution we test is Azure-based. Here is setup information for Azure B2C on iOS. As often, there is a lot of documentation and sample code, but the code is sometimes not maintained or the documentation can be misleading.

For a good description of OAuth, OpenID and OpenID Connect, you can check this Stack Exchange thread.

Extensive documentation about PKCE is available online, for example here, here or [here]().

A useful playground is available to understand OAuth flows, in particular the PKCE flow.

I initially thought we would need an accessToken / refreshToken pair, but apparently we only need to get an idToken. As discussed with our potential provider:

The goal is to recieve an id_token, which contains the data about the user. A refresh token can be received with the additional scope offline_access. An access token is not needed and can be gotten if you add an additional scope, that has been been predefined. The id_token can be verified with the public key available from the metadata

An access token would only be needed to access restricted scopes, like writing to the azure b2c directory, which is probably not needed by your application.

Id_token is the main concept in azure b2c, to transport the user information. Access token can have role depending on the solution design. The time to live or session can depend on the security requirements of the applications and the backend. An eternally login in person can be a problem, because you can never know if the person that orginally logged in is still the current user.

It is therefore likely we do not need the whole authentication flow to be implemented (in particular authentication exchange in PKCE is not needed). In fact we probably do not need PKCE at all.

Client configuration

The client can be configured in two ways:

The first approach introduces an additional request and is therefore a bit slower. The choice is therefore between compactness / convenience and a slightly faster user experience.

URL scheme

iOS 11 introduced SFAuthenticationSession, later replaced with ASAuthenticationSession in iOS 12. These two APIs are meant for secure in-app authentication, without the need for the application to support a declared URL scheme to redirect the user to the app at the end of the login procedure.

Prior to iOS 11, application had to use SFSafariViewController and a declared URL scheme to manage the authentication process.

Nonetheless, a redirect URL with a custom scheme declared in the application Info.plist file is required by the AppAuth SDK. Whether we actually leave the app and return with a custom scheme (iOS 11 and below) or stay within the app (iOS 12 and above), OAuth requires a redirect_uri and this URL is checked by the AppAuth SDK when the redirect is handled.

Server configuration

The server-side required configuration is minimal:

SRG Identity integration

The AppAuth SDK does not handle everything we need for easy integration in SRG SSR apps. I therefore recommend we keep our SRG Identity library, using AppAuth to manage all low-level authentication details.

Project integration

The AppAuth SDK supports Carthage and is therefore a perfect fit. It moreover already supports tvOS, watchOS and Catalyst, which will not prevent us from improving our SDK in the future.

If we just need to manage an identity token, though, it might be superfluous to even integrate the AppAuth SDK altogether. I am not sure this is a good idea, but this is something we could at least discuss.

Initialization

Currently URLs are supplied at initialization time. In the future a single SRG SSR service will handle login, therefore we should probably hide this complexity and hardcode the OpenID configuration URL (or the authorization and token URLs). Alternatively, we can provide NSURL constants, and keep URL parameters.

We probably can retain the multiple service support as well if this makes sense. We currently were adding a UUID parameter to the redirect URL to match a redirect with the SRGIdentityService which initiated the request in the first place. Maybe the service whitelist allow wildcards so that the last path component of a URL redirect can be customized, but it is likely that when the OIDExternalUserAgentSession flow is resumed this check is made automatically.

If this is not possible, we can stil turn our service into a singleton with a single hardcoded URL.

State persistence

The AppAuth SDK retrieves an OIDAuthState which can be securely encoded. SRG Identity must persist this information.

There is no logout concept in the AppAuth SDK. To logout a user it suffices to remove the stored OIDAuthState.

The OpenID configuration might apparently expose end_session_endpoint. The AppAuth SDK parses this information but never uses it, see kEndSessionEndpointKey in the source code.

Token freshness

The SDK manages access token freshness with a refresh token, but since we only have an identity token in our case (see above) there is no associated refresh after expiry (currently 1 hour). It is to me still unclear what the user experience will be when we actually need to renew this token. I would expect the user to have to login only very rarely, but I am not sure how this will be implemented. In any case, this should probably be a responsibility of SRG Identity.

Error handling

We should probably implement basic error handling, most notably to check if authorization code exchange delivers the expected tokens (according to my tests, if the server does not have PKCE enabled, the process does not fail but no tokens are retrieved). Either there is a way to identify such issues early (after all, the information is in the OpenID configuration file), or we should check that tokens are not nil.

Redirect URL

Supporting the legacy SFSafariViewController authentication flow would not be required for iOS 12 and above, and there would be in principle no need for applications to declare at least one supported URL scheme to handle redirects properly. Still, the SDK uses the legacy approach on iOS 12 and above because of an issue with guided access.

Therefore, applications will need to declare a URL scheme even if they are compatible with iOS 12 and above only, and we will probably have to add each one of these URLs to the server whitelist so that the authentication flow can end properly.

Therefore, SRG Identity will require at least one custom scheme to be defined by each application integrating it, and a different one. Each one of the associated redirect URLs will need to be addded to the server whitelist so that the authentication flow can resume properly in the correct app when the legacy approach is used.

According to our OS support policy, the updated SDK will likely support iOS 12 and above. Still, if we haven't made the move from iOS 9 then, we might preserve iOS 9 support, as the work required is probably the same. This of course will be discussed within the team.

Authentication warning message

The AppAuth SDK internally chooses between ASAuthenticationSession and legacy methods. In previous SRG Identity implementations, we optionally provided a way to force legacy variants because some of our clients feared the warning message displayed when using ASAuthenticationSession would scare users.

Screenshot 2020-03-27 at 15 56 38 1

In the future, this feature will not be available anymore. This is part of the now usual iOS experience and we should embrace it.

Token refresh

Our current SRG Identity implementation exposes a -reportUnauthorization method, with which suspicions of revoked tokens can be reported. With the AppAuth SDK and short-lived tokens, this approach must be dropped.

Instead, we should expose the AppAuth -performActionWithFreshTokens: method so that clients proactively ensure tokens are fresh before using them. For the same reason, we should not provide any direct token properties, thus enforcing the use of this method.

User account information

Previously, user account information was retrieved with a dedicated service. With the Azure-based implementation we could test, user information is directly available within the JWT id token. Our SRG Identity library should therefore embed a JWT decoder and parse this information. Many iOS implementations exist, for example here and there, even in Objective-C. This also avoids the need for a dedicated webservice request since the token is acquired as part of the usual authentication flow.

Profile page

To be discussed.

tvOS support

According to this AppAuth Github issue, there is no standard for authentication on tvOS, only a draft. Nonetheless, some implementations are available, see this comment in particular.

I suggest we implement this missing piece in SRG Identity directly based on the existing implementation proposals above. I would rather avoid forking AppAuth for such a change, which would make updates painful afterwards.

Once an official implementation is available (which will probably take some time), we will be able to drop our implementation, or to rewrite it based on the official one.

defagos commented 4 years ago

Well, to get access and refresh tokens, it suffices to add the client id to the scope list.