OAuth2Observer
open class OAuth2Observer
extension OAuth2Observer: SAPURLSessionObserving
OAuth2Observer
The use of this component is the SDK-suggested way of implementing OAuth 2 authentication in the application.
The OAuth2Observer
allows a transparent OAuth authentication for HTTP requests.
It sets the OAuth HTTP header on requests before they are sent.
Moreover, it can detect failures that occur during OAuth authentication and handle authentication failures.
It uses an OAuth2Authentication
instance to authenticate and to refresh tokens.
For token storing the OAuth2TokenStore
is used.
OAuth 2 is an open standard for authorization and enables applications to obtain controlled access to protected resources, for instance, a HTTP or OData service.
This component currently supports:
Authorization Code Grant
flow with theBearer token
type.Client Credentials Grant
flow with HTTP Basic authentication type (or custom).
Usage
There are several combination of configurations how the OAuth authentication can be implemented using the SDK.
SDK-suggested way
The most common and sdk-suggested way to implement the authentication is the use of the convenience initializers.
Only the OAuth2AuthenticationParameters
and an instance of SAPURLSession
is required beforehand.
Authorization Code Grant
// Create the wrapper with the server side configuration
let authorizationEndpointURL = URL(string: "<#URL String#>")!
let tokenEndpointURL = URL(string: "<#URL String#>")!
let tokenRedirectURL = URL(string: "<#URL String#>")!
let clientID = "<#Client ID hash#>"
let scopes = Set<String>()
let parameters = OAuth2AuthenticationParameters(authorizationEndpointURL: authorizationEndpointURL, clientID: clientID, redirectURL: tokenRedirectURL, tokenEndpointURL: tokenEndpointURL, requestingScopes: scopes)
// Create the OAuth2Observer using the convenience initializer
let observer = OAuth2Observer(authenticationParameters: parameters, tokenStore: self)
// Register the newly created observer
sapURLSession.register(observer)
Client Credentials Grant
// Create the wrapper with the server side configuration
let tokenEndpointURL = URL(string: "<#URL String#>")!
let scopes = Set<String>()
let name = "<#Name#>"
let secret = "<#Secret#>"
let parameters = OAuth2ClientCredentialsAuthenticationParameters(tokenEndpointURL: tokenEndpointURL, requestingScopes: scopes, name: name, secret: secret)
// Create the OAuth2Observer using the convenience initializer
let observer = OAuth2Observer(clientCredentialsAuthenticationParameters: parameters, tokenStore: OAuth2TokenStorage())
// Register the newly created observer
sapURLSession.register(observer)
The example above uses the default OAuth2TokenStorage
shipped with the SDK.
It is assumed to have a sapURLSession
which is an instance of SAPURLSession
.
By following this method, the OAuth2Authenticator
is implicitly instantiated with a new instance of SAPURLSession
.
Note: The OAuth2Authenticator
uses SFSafariViewController
by default.
See more at Using the default web view presenter section of the OAuth2Authenticator
.
Customization
There can be scenarios where the SDK supplied convenience implementation is not sufficient. The SDK supports multiple level of customizations. You can find examples at the Authentication section.
Implementing OAuth2TokenStore
A default implementation of the protocol is available in the SDK: OAuth2TokenStorage.
This sample code demonstrates what a very basic (in-memory) store implementation might look like:
private let accessQueue = DispatchQueue(label: "OAuth2TokenAccessQueue", attributes: .concurrent)
private var tokens = [String: OAuth2Token]()
public func storeToken(_ token: OAuth2Token, for url: URL) {
accessQueue.sync(flags: .barrier) {
self.tokens[url.host!] = token
}
}
public func token(for url: URL) -> OAuth2Token? {
return accessQueue.sync {
return self.tokens[url.host!]
}
}
public func deleteToken(for url: URL) {
accessQueue.sync(flags: .barrier) {
self.tokens[url.host!] = nil
}
}
Triggering
By default the OAuth2Observer
reacts to the HTTP 401 response status code, and to the HTTP 400 with X-SMP-AUTHENTICATION-STATUS: 1000
header which may be sent by SAP Mobile Services, if the authorization token is missing or wrong.
When custom challenge decision logic is required, you can override the OAuth2 observer’s external challenge decision making method. The following example shows how you can override the default logic:
class CustomOAuth2Observer: OAuth2Observer {
override open func isChallenge(dataTask: SAPURLSessionTask, response: URLResponse) -> Bool {
// Check the response, and decide if it is an OAuth2 challenge or not.
// Return true in case of an identified OAuth2 challenge
return true
}
}
Failure handling
Authentication failures can happen for multiple reasons:
- There is no OAuth2 token present on the request.
- The sent OAuth2 token is invalid or expired.
The OAuth2Observer
supports two ways for handling an authentication failure:
- Authenticate
- Refresh token
After the first failure, if there is no token in the OAuth2TokenStore
, then the authentication process starts.
After a successful authentication, the original request is resent.
If there is a token in the OAuth2TokenStore
, then the refresh process starts.
After a successful refresh, the original request is resent.
Multiple requests
If multiple requests try to authenticate at once, only the first proceeds and the others wait for the result. In case of authentication failure, the next request in the queue tries to authenticate again. It is the caller’s responsibility to cancel all other active requests in case of authentication failure.
Note - regarding the main thread
The observer is aware of the application state, and does not allow to run UI-required authentication in background state.
It uses the main thread synchronously to read the UIApplication.shared.applicationState
property.
Do not use the main thread to wait (block) for a network request which goes through this observer!
The use of this component is the SDK-suggested way of implementing OAuth 2.0 authentication in the application.
The OAuth2Observer
allows a transparent OAuth authentication for HTTP requests.
It sets the OAuth HTTP header on requests before they are sent.
Moreover, it can detect failures that occur during OAuth authentication and handle authentication failures.
It uses an OAuth2Authentication
instance to authenticate and to refresh tokens.
For token storing the OAuth2TokenStore
is used.
This component supports:
Authorization Code
grant flow with theBearer token
type.Client Credentials
grant flow.
Example use:
// Create the wrapper with the server side configuration
let authorizationEndpointURL = URL(string: "<#URL String#>")!
let tokenEndpointURL = URL(string: "<#URL String#>")!
let tokenRedirectURL = URL(string: "<#URL String#>")!
let clientID = "<#Client ID hash#>"
let parameters = OAuth2AuthenticationParameters(authorizationEndpointURL: authorizationEndpointURL, clientID: clientID, redirectURL: tokenRedirectURL, tokenEndpointURL: tokenEndpointURL)
// Create the OAuth2Observer using the convenience initializer
let observer = OAuth2Observer(authenticationParameters: parameters, tokenStore: self)
// Register the newly created observer
sapURLSession.register(observer)
The example above assumes that the class in which it is executed implements the OAuth2TokenStore
protocol, therefore it can pass self
as the tokenStore
.
It is also assumed to have a sapURLSession
which is an instance of SAPURLSession
.
-
when set the observer calls this handler after a new authentication but before the authentication process finishes and all other requests continues
Declaration
Swift
public var authenticationHandler: AuthenticationHandling?
-
Instantiates the observer based on the authentication parameters. You can also provide an OAuth2TokenStore or use the default value. The authenticator is implicitly created with a new instance of SAPURLSession.
Declaration
Swift
public init(authenticationParameters: OAuth2AuthenticationParameters, tokenStore: OAuth2TokenStore)
Parameters
authenticationParameters
The necessary parameters to authenticate and refresh a token. Parameters contain server related information.
tokenStore
the
OAuth2TokenStore
implementation which can provide and store the obtained token. -
Undocumented
Declaration
Swift
public init(authenticator: OAuth2Authentication, tokenStore: OAuth2TokenStore)
-
External OAuth 2.0 challenge decision logic. The default behaviour checks the status code of the response. When the status code is 400 or 401, the authentication (or refresh) flow will start, otherwise this observer is skipped. The method can be overriden if custom challenge decision logic is needed. The OAuth2 observer will launch the authentication (or refresh) flow if this method returns
true
.Declaration
Swift
open func isChallenge(dataTask: SAPURLSessionTask, response: URLResponse) -> Bool
Parameters
dataTask
the resumed SAPURLSessionTask
response
the received URLResponse
Return Value
True
is the response indicates an OAuth2 challenge,False
otherwise -
Declaration
Swift
public func sapURLSession(_ session: SAPURLSession, task: SAPURLSessionTask, willSend request: URLRequest, completionHandler: @escaping (SAPURLSession.RequestDisposition) -> Void)
-
Declaration
Swift
public func sapURLSession(_ session: SAPURLSession, task: SAPURLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, completionHandler: @escaping (SAPURLSession.HTTPRedirectDisposition) -> Swift.Void)
-
Declaration
Swift
public func sapURLSession(_ session: SAPURLSession, dataTask: SAPURLSessionTask, didReceive response: URLResponse, completionHandler: @escaping (SAPURLSession.ResponseDisposition) -> Void)
-
Declaration
Swift
public func sapURLSession(_ session: SAPURLSession, downloadTask: SAPURLSessionTask, didFinishDownloadingTo location: URL, completionHandler: @escaping (SAPURLSession.DidCompleteDisposition) -> Void)
-
Declaration
Swift
public func sapURLSession(_ session: SAPURLSession, task: SAPURLSessionTask, didCompleteWithError error: Error?, completionHandler: @escaping (SAPURLSession.DidCompleteDisposition) -> Void)
-
Declaration
Swift
public func sapURLSession(_ session: SAPURLSession, task: SAPURLSessionTask, didCompleteWithError error: Error?)
-
Undocumented
Declaration
Swift
public func copy() -> Any?
-
Instantiates the observer based on the authentication parameters. You can also provide an OAuth2TokenStore or use the default value. The authenticator is implicitly created with a new instance of SAPURLSession.
Declaration
Swift
@available(iOSApplicationExtension, unavailable) public convenience init(clientCredentialsAuthenticationParameters authenticationParameters: OAuth2ClientCredentialsAuthenticationParameters, tokenStore: OAuth2TokenStore)
Parameters
clientCredentialsAuthenticationParameters
The necessary parameters to authenticate and refresh a token. Parameters contain server related information.
tokenStore
the
OAuth2TokenStore
implementation which can provide and store the obtained token.