Open defagos opened 4 years ago
The following documents my findings and recommendations if we want to move forward with this approach.
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:
NSSecureCoding
.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.
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.
The client can be configured in two ways:
.well-known/openid-configuration
server endpoint, remove these two last components, and you have it.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.
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.
The server-side required configuration is minimal:
code_challenge_methods_supported
setting in this case, see for example Google Accounts OpenID configuration. The full RFC is available for more information.~ (Probably not required after all)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.
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.
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.
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.
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.
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
.
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.
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.
In the future, this feature will not be available anymore. This is part of the now usual iOS experience and we should embrace it.
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.
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.
To be discussed.
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.
Well, to get access and refresh tokens, it suffices to add the client id to the scope list.
We may change our existing login provider. This issue discusses some finds and ideas for the future, provided we continue in this direction.