Identity providers
The iOS SDK allows you to choose between multiple identity providers (IDP).
The platform supports two main types of identity providers: browser and custom API. Both types of identity providers can be defined and configured in the IDAAS-core configuration. All identity providers that are configured in the app are returned by the iOS SDK as IdentityProvider
.
For an identity provider to be available, it must be configured in the IDAAS-core. The list of available identity providers can be fetched using the identityProviders
method on a UserClient
implementation instance.
Example code: fetch a set of IdentityProviders
let identityProviders = SharedUserClient.instance.identityProviders
NSSet <ONGIdentityProviders *> *identityProviders = [ONGUserClient sharedInstance].identityProviders;
The class representing an identity provider is called IdentityProvider
. Objects of that class are DTOs, when returned by the iOS SDK, their property values will not be changed or updated. IdentityProvider has the following properties:
- The identifier
String
is a unique identifier defined in the IDAAS-core admin panel. - The name
String
is a human-readable name defined in the IDAAS-core admin panel. - The externalIdentityProvider
ExternalIdentityProvider
is an external identity provider.
External identity providers are represented by the ExternalIdentityProvider
class, which has the following properties:
- The name
String
is the name of an external identity provider. - The type
String
is the type of an external identity provider.
An instance of IdentityProvider
can be used when initiating user registration to select an identity provider instead of using the default one picked by the IDAAS-core.
let identityProviders = SharedUserClient.instance.identityProviders
let yourIdentityProvider = identityProviders.first // your implementation of choosing it
SharedUserClient.instance.registerUserWith(identityProvider: yourIdentityProvider,
scopes: ["read"],
delegate: self)
NSSet <ONGIdentityProviders *> *identityProviders = [ONGUserClient sharedInstance].identityProviders;
ONGIdentityProvider *yourIdentityProvider = [identityProviders filteredSetUsingPredicate:yourPredicate].anyObject;
[[ONGUserClient sharedInstance] registerUserWithIdentityProvider:yourIdentityProvider scopes:yourScopes delegate:self];
Browser IDP
To support registering with a browser-based IDP, the iOS SDK must request an access grant from the IDAAS-core using the browser. The iOS SDK can accomplish this using the BrowserRegistrationChallenge
to request the access grant in a separate web browser.
When registering a user the application will receive a browser registration challenge containing a URL that must be handled. The challenge will be passed as an argument to the userClient(_:didReceiveBrowserRegistrationChallenge:)
method of your RegistrationDelegate
. This URL will redirect the user to a web page where they can authenticate to their user account. When authentication is successful, the page will redirect you to your redirect URL, which is defined in the application configuration.
The userClient(_:didReceiveBrowserRegistrationChallenge:)
method has two parameters:
- The userClient (
UserClient
) is the user client performing registration. - The challenge (
BrowserRegistrationChallenge
) is the challenge containing the URL used to perform a registration code request.
The challenge object represents the browser registration challenge. It provides all information about the challenge and the sender waiting for the response:
- The userProfile (
UserProfile
) is the user profile for which the registration request challenge was sent. - The identityProvider (
IdentityProvider
) is the identity provider used to register the user. - The URL (
URL
) is used to perform a registration code request. - The error (
Error
) describes the cause of failure of the previous challenge response. Possible error domains are:ONGGenericErrorDomain
. - The sender (
BrowserRegistrationChallengeSender
) is the sender waiting for a response to the registration request challenge.
You can respond to the challenge using a sender object by calling one of the following methods:
respond(with:to:)
is used to deliver the redirection URL to the iOS SDK.cancel(_ challenge)
is used for registration cancellation.
It's up to you how you want to handle this URL, however it's recommended to use a web browser like Safari or preferably an embedded web browser like UIWebView.
We recommend using an embedded UIWebView to open this URL because this is the least disruptive for the user and also benefits from the additional security measures included in the iOS SDK. The iOS SDK from release 2.3 and upwards is updated to intercept the URL requests from the embedded UIWebView and perform certificate pinning.
Handle a browser registration URL with UIWebView
You can perform a request using UIWebView
by using the loadRequest
method.
let request = URLRequest(url: browserRegistrationChallenge.url)
webview.load(request)
NSURLRequest *request = [NSURLRequest requestWithURL:self.browserRegistrationChallenge.url];
[self.webView loadRequest:request];
The web view should guide the user through the authentication process and then redirect back to the application using the redirect URL. To intercept this redirection, it is recommended to implement the webView(_:shouldStartLoadWith:navigationType:)
method on your UIWebViewDelegate
. Example implementation:
func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebView.NavigationType) -> Bool {
if request.url!.absoluteString.hasPrefix(configModel.redirectURL) {
browserRegistrationChallenge.sender.respond(with: request.url, to: browserRegistrationChallenge)
self.navigationController.dimiss
return false
}
return true
}
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
if ([request.URL.absoluteString hasPrefix:[OneginiConfigModel configuration][@"ONGRedirectURL"]]) {
[self.browserRegistrationChallenge.sender respondWithURL:request.URL challenge:self.browserRegistrationChallenge];
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
return NO;
}
return YES;
}
It's up to you when the web view will be closed. You could close it as soon as challenge sender receives the redirection URL, which is shown in the example, or when your RegistrationDelege
receives userClient(_:didReceivePinRegistrationChallenge:)
.
Handle a browser registration URL with an external web browser
To handle a registration request using an external web browser, you first need to configure a custom URL scheme.
After the user has authenticated, they are redirected back to the app using a custom URL scheme. The app must add an active URL scheme to the info.plist
file. Administration of the redirect URL (that includes the custom URL scheme) is done in the application configuration in the IDAAS-core admin console.
Below you can see the contents of the URL type configuration in Xcode:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>com.onegini.OneginiSDKiOSTestApp</string>
<key>CFBundleURLSchemes</key>
<array>
<string>oneginisdk</string>
</array>
</dict>
</array>
</dict>
</plist>
To open the URL using an external web browser, use the openURL
method of the UIApplication
shared instance.
func userClient(_ userClient: UserClient,
didReceiveBrowserRegistration challenge: BrowserRegistrationChallenge) {
UIApplication.shared.open(challenge.url)
}
- (void)userClient:(ONGUserClient *)userClient didReceiveBrowserRegistrationChallenge:(ONGBrowserRegistrationChallenge *)challenge
{
[[UIApplication sharedApplication] openURL:challenge.url];
}
Redirection using a custom URL scheme must be implemented within your UIApplicationDelegate
object. It's done by implementing one of the following methods:
application:openURL:options:
is available from iOS 9application:handleOpenURL:
is deprecated, but available before iOS 9application:openURL:sourceApplication:
is deprecated, but available before iOS 9
func application(_ app: UIApplication,
open url: URL,
options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
if request.url!.absoluteString.hasPrefix(configModel.redirectURL) {
browserRegistrationChallenge.sender.respond(with: request.url, to: browserRegistrationChallenge)
self.navigationController.dimiss
return false
}
return true
}
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<NSString*, id> *)options
{
if ([request.URL.absoluteString hasPrefix:[OneginiConfigModel configuration][@"ONGRedirectURL"]]) {
[self.browserRegistrationChallenge.sender respondWithURL:request.URL challenge:self.browserRegistrationChallenge];
return YES;
}
return NO;
}
Custom IDP
To support registering with an IdP that has a custom API, the iOS SDK must use the custom registration functionality delivered by the application. To use a custom IDP, it must be enabled and configured in the IDAAS-core and implemented in the application. Usually when the custom IdP is created on the server side, you will also provide the server-side script for handling the registration process on both the client and server side.
One-step registration
- App -> SDK: start user registration with selected custom IDP
- SDK -> App: ask for registration data
- App -> SDK: provide registration data
- SDK -> IDAAS-core: send registration data
- IDAAS-core -> XE: register user
- XE -> IDAAS-core: send registration result
- IDAAS-core -> SDK: send registration result
- SDK -> App: send registration result
In case of one-step registration, the iOS SDK asks the app for optional registration data. The data is sent to the IDAAS-core where the custom registration script is executed. The result of the custom script (status code and optional data) is sent back to the iOS SDK and the registration result is propagated to the app.
Two-step registration
In case of two-step registration, at the first step the iOS SDK asks the app for an optional initial registration data. The data is sent to the IDAAS-core where the custom registration is initialized. Then iOS SDK receives optional initialization result provided by the IDAAS-core and asks the app for a optional registration data. The registration data is sent to the IDAAS-core where the custom registration script is executed. The result of the custom script (status code and optional data) is sent back to the iOS SDK and the registration result is propagated to the app.
- App -> SDK: start user registration with selected custom IDP
- SDK -> App: ask for initialization data
- App -> SDK: provide initialization data
- SDK -> IDAAS-core: send initialization data
- IDAAS-core -> XE: initialize registration
- XE -> IDAAS-core: provide initialization result
- IDAAS-core -> SDK: provide initialization result
- SDK -> App: ask for registration data
- App -> SDK: provide registration data
- SDK -> IDAAS-core: send registration data
- IDAAS-core -> XE: register user
- XE -> IDAAS-core: send registration result
- IDAAS-core -> SDK: send registration result
- SDK -> App: send registration result
Handle a custom registration challenge
When registering a user, the application will receive a custom registration challenge containing a used identity provider, which must be handled. The challenge will be passed as an argument to the userClient(_:didReceiveCustomRegistrationInitChallenge:)
and userClient(_:didReceiveCustomRegistrationFinishChallenge:)
methods of your RegistrationDelegate
.
Depending on the registration type used for an identity provider, the handling is performed slightly differently:
- One-step: The iOS SDK will call only the
userClient(_:didReceiveCustomRegistrationFinishChallenge:)
method on your delegate. - Two-step: The first the iOS SDK will call
userClient(_:didReceiveCustomRegistrationInitChallenge:)
to handle the first step. The second step will begin when the first step completes,. The iOS SDK will calluserClient(_:didReceiveCustomRegistrationFinishChallenge:)
.
Both userClient(_:didReceiveCustomRegistrationInitChallenge:)
and userClient(_:didReceiveCustomRegistrationFinishChallenge:)
methods have two parameters:
- The userClient (
UserClient
) is the user client performing the registration. - The challenge (
CustomRegistrationChallenge
) is the challenge containing the selected identity provider used to perform a registration.
The challenge object represents a custom registration challenge. It provides all information about the challenge and the sender waiting for the response:
- The userProfile (
UserProfile
) is the user profile for which the custom registration challenge was sent. - The identityProvider (
IdentityProvider
) is the identity provider used for registration. - The info (
CustomInfo
) is optional data used to perform a registration. - The error (
Error
) describes the cause of failure of the previous challenge response. Possible error domains are:ONGGenericErrorDomain
. - The sender (
CustomRegistrationChallengeSender
) is the sender waiting for a response to the custom registration challenge.
You can respond to the challenge using the sender object by calling one of the following methods:
respondWithData:challenge
is used to deliver the optional data to the iOS SDK.cancel
is used for registration cancellation.
The data used to respond to the challenge is sent to the IDAAS-core. When there are no issues detected, the iOS SDK will proceed by sending the next challenge. In case of error, IDAAS-core specifies two groups of error codes that are returned within the ONGCustomInfo
object:
- Recoverable errors (4000 - 4999): The registration flow can be recovered. The iOS SDK will call either the
userClient(_:didReceiveCustomRegistrationInitChallenge:)
oruserClient(_:didReceiveCustomRegistrationFinishChallenge:)
method again. Depending on the step that has caused the error, it may permit the user to retry the operation. - Unrecoverable errors (5000 - 5999): The registrations flow cannot be recovered and must be restarted.
Example code: custom registration initialization step
func userClient(_ userClient: UserClient,
didReceiveCustomRegistrationInitChallenge challenge: CustomRegistrationChallenge) {
if challenge.identityProvider.identifier == "2-way-otp-api" {
challenge.sender.respond(with: "optional data", to: challenge)
} else {
challenge.sender.cancel(challenge)
}
}
- (void)userClient:(ONGUserClient *)userClient didReceiveCustomRegistrationInitChallenge:(ONGCustomRegistrationChallenge *)challenge
{
// Continue user registration with optional data
[challenge.sender respondWithData:@"optional data" challenge:challenge];
// Or cancel challenge
[challenge.sender cancelChallenge:challenge];
}
Example code: custom registration handling step
func userClient(_ userClient: UserClient,
didReceiveCustomRegistrationFinishChallenge challenge: CustomRegistrationChallenge) {
if challenge.identityProvider.identifier == "2-way-otp-api" {
challenge.sender.respond(with: "optional data", to: challenge)
} else {
challenge.sender.cancel(challenge)
}
}
- (void)userClient:(ONGUserClient *)userClient didReceiveCustomRegistrationFinishChallenge:(ONGCustomRegistrationChallenge *)challenge
{
// Continue user registration with optional data
[challenge.sender respondWithData:@"optional data" challenge:challenge];
// Or cancel challenge
[challenge.sender cancelChallenge:challenge];
}