New implementation of the Schibsted account iOS SDK using the web flows via
ASWebAuthenticationSession
.
API documentation can be found here.
To implement login with Schibsted account in your app, please first have a look at our documentation. This will help you create a client and configure the necessary data.
Note: This SDK requires your client to be registered as a public_mobile_client
in Self Service (see the mobile sdk's dedicated section for more help).
- iOS 12.0+
Swift Package Manager: .package(url: "https://github.com/schibsted/account-sdk-ios-web")
CocoaPods pod 'AccountSDKIOSWeb'
let clientConfiguration = ClientConfiguration(environment: .pre,
clientId: clientId,
redirectURI: redirectURI)
let client = Client(configuration: clientConfiguration)
let contextProvider = ASWebAuthSessionContextProvider()
let asWebAuthSession = client.getLoginSession(contextProvider: contextProvider, withSSO: true, completion: { result in
switch result {
case .success(let user):
print("Success - logged in as \(String(describing: user.uuid))")
self.user = user
case .failure(let error):
print(error)
}
user.fetchProfileData { result in
switch result {
case .success(let userData):
print(userData)
case .failure(let error):
print(error)
}
}
})
asWebAuthSession.start()
let userDelegate: UserDelegate = MyUserDelegate()
user?.delegates.addDelegate(userDelegate)
self.userDelegate = userDelegate // Needs to be retained
class MyUserDelegate: UserDelegate {
func userDidLogout() {
print("Callback will be invoked when user is logged out")
}
}
To support TCF consents exchange between native and WebView, we have added a new method to the SDK, which allows you to obtain an externalId
for the consent sharing support.
To get the externalId
, you need to call the following method:
let externalId = client.getExternalId(pairId: String, externalParty: String, optionalSuffix: String = "")
pairId
can be found under UserProfileResponse.pairId
property.
When using custom URI as redirect URI, the OS handles opening the app associated with the link instead of triggering the ASWebAuthenticationSession
callback.
It results in the ASWebAuthenticationSession
view not being closed properly, which instead needs to be done manually:
-
Get a reference to
ASWebAuthenticationSession
and start it:func handleLoginResult(_ result: Result<User, LoginError>) { switch result { case .success(let user): print("Success - logged-in as \(user.uuid)!") self.user = user case .failure(let error): print(error) } } let contextProvider = ASWebAuthSessionContextProvider() asWebAuthSession = client.getLoginSession(contextProvider: contextProvider, withSSO: true, completion: handleLoginResult) asWebAuthSession.start() // this will trigger the web context asking the user to login
-
Handle the response as an incoming URL, e.g. via your app's delegate
application(_:open:options:)
:func application(_ application: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:] ) -> Bool { client.handleAuthenticationResponse(url: url) { result in DispatchQueue.main.async { asWebAuthSession.cancel() // manually close the ASWebAuthenticationSession } handleLoginResult(result) } }
-
When implementing Swedish BankID authentication the parent app must catch the redirect URI and return with no action. Please make sure that
handleAuthenticationResponse
is not called for the BankID redirect. The URI scheme that is used to redirect back to the parent app from BankID will have the following format:{app_uri_scheme}:/bankId
. Find below a code example:func handleOnOpenUrl(url: URL) { if url.pathComponents.contains("bankId") { return } client.handleAuthenticationResponse(url: url) { result in DispatchQueue.main.async { asWebAuthSession.cancel() // manually close the ASWebAuthenticationSession } handleLoginResult(result) } }
Tokens can be obtained externally and injected into SDK for the already created users. This can be useful in the case of a test scenario. To do this, first you need to start the web login flow with the request as follow:
GET "${BASE_URL}/oauth/authorize?client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}&response_type=code&scope=openid%20offline_access&state=${state}&nonce=${nonce}&code_challenge=${CODE_CHALLENGE}&code_challenge_method=S256&prompt=select_account"
where:
BASE_URL
- base URL of Schibsted Account environment
client_id
- public mobile client id
redirect_uri
- redirect URI for given client
state
- A string value sent by the client that protects end user from CSRF attacks. It's a randomly generated string of 10 characters (both letters and numbers)
nonce
- A string value sent by the client that is included in the resulting ID token as a claim. The client must then verify this value to mitigate token replay attacks. It's a randomly generated string of 10 characters (both letters and numbers)
code_challenge
- PKCE
calculated from code_verifier
On the finish, web flow returns URL with query parameters state
and code
.
Tokens can be obtained with the following request:
curl {BASE_URL}/oauth/token \
-X POST \
-H "X-OIDC: v1" \
-d "client_id={client_id}" \
-d "grant_type=authorization_code" \
-d "code={code_from_login_flow}" \
-d "code_verifier={code_verifier}" \
-d "redirect_uri={redirect_uri}"
where code_verifier
is the same which was used for calculating code_challenge
. It can be a randomly generated string of 60 characters (both letters and numbers)
This SDK uses SwiftLog
, allowing you to easily customise the logging.
The logger can be modified, for example to change the log level, via the following code:
SchibstedAccountLogger.instance.logLevel = .debug
This SDK implements the best practices for user authentication via an OpenID Connect identity provider:
- It uses
ASWebAuthenticationSession
. This allows for single-sign on between apps, with the user being recognized as a returning user to Schibsted account via cookies. On iOS 13 and above this behavior can be disabled, which also removes the extra user prompt about allowing to use Schibsted account for login, usingwithSSO: false
inClient.getLoginSession(withMFA:loginHint:extraScopeValues:withSSO:completion:)
. - After the completed user authentication, user tokens are obtained and stored securely in the keychain storage.
-
The ID Token is validated according to the specification. The signature of the ID Token (which is a JWS) is verified by the library
JOSESwift
. -
Authenticated requests to backend services can be done via
AuthenticatedURLSession.dataTask(with: URLRequest, completionHandler: ...
The SDK will automatically inject the user access token as a Bearer token in the HTTP Authorization request header. If the access token is rejected with a401 Unauthorized
response (e.g. due to having expired), the SDK will try to use the refresh token to obtain a new access token and then retry the request once more.Note: If the refresh token request fails, due to the refresh token itself having expired or been invalidated by the user, the SDK will log the user out.
-
- Upon opening the app, the last logged-in user can be resumed by the SDK by trying to read previously stored tokens from the keychain storage. This will be handled by once invoking
Client.resumeLastLoggedInUser(completion: @escaping (User?) -> Void)
upon app start.
Prerequisite: Applications need to be on the same Apple Development account in order to have access to the shared keychain.
- In your application target, add Keychain Sharing capability with keychain group set to
com.schibsted.simplifiedLogin
. - Creating client, pass additional parameter
appIdentifierPrefix
. It is usually the same as team identifier prefix - 10 characters combination of both numbers and letters assigned by Apple.
let client = Client(configuration: clientConfiguration, appIdentifierPrefix: "xxxxxxxxxx")
- Create
SimplifiedLoginManager
and callgetSimplifiedLogin
method anytime you want to present SL to user.
let context = ASWebAuthSessionContextProvider()
let manager = SimplifiedLoginManager(client: client, contextProvider: context, env: clientConfiguration.env) { result in
print("Catch login result \(result)")
}
manager.requestSimplifiedLogin() { result in
switch (result) {
case .success():
print("success")
case .failure(let error):
print("Catch error from presenting SL \(error)")
}
}
If you want to present Simplified Login UI on a specific UIWindow, you need to pass the optional parameter window
calling requestSimplifiedLogin
method.
The Account SDK does some internal tracking (mostly for the Simplified Login) and allows a TrackingEventsHandler
to be set during the Client's initialization.
To fulfill this, you can either implement it yourself or use one which is already implemented.
Internal: There is an internal Schibsted Tracking implementation for the identity SDK available here It integrates the latest Account SDK with the latest Pulse Tracking SDK for iOS.
Simplified Login comes with the following localization support:
- 🇳🇴 Norwegian Bokmål
- 🇸🇪 Swedish
- 🇫🇮 Finnish
- 🇩🇰 Danish
- 🇬🇧 English (Default)
- All you need is Xcode with command-line tools installed. It works perfectly with version 13.4.1.
- Clone repository
- Open
workspace.xcworkspace
file, which exists in the Projects folder. It contains two projects inside: AccountSDKIOSWeb which is the SDK itself, and ExampleWeb which serves as a demo application. - To run the demo application on the simulator, choose the ExampleWeb scheme and target your choices like an iPhone or iPad. Run the application with the play button or press
command + R
. - If Xcode fails to resolve package dependency, click on them and resolve them manually.
- There are two schemes with tests. To run unit tests, select one of them and press
command + U
- To successfully release the SDK's pod to the CocoaPods repository, you need first set up the pod trunk on your computer. See https://guides.cocoapods.org/making/getting-setup-with-trunk.html.
- You should be added as an owner to the library in the CocoaPods repository. Please ask the User Access team, who can give you the correct rights.
- Make sure all changes going in the release have been merged to the
master
branch. - Update new SDK version number in both Version.swift and AccountSDKIOSWeb.podspec files. Commit this change to the
master
branch. - Create a new release via GitHub.
- Enter the version number as the tag name, and include all important changes in the release description.
- Publish the pod by running
pod trunk push AccountSDKIOSWeb.podspec
from your local machine.