iOS local login

Table of Contents:


Demo App Analysis:



  • XCode

Downloading the repo

Then download the iOS SDK repo from

Then checkout the 'MFA' branch:

git checkout MFA

Building the SDK

Note that the repo includes a submodule. So after downloading run:

cd sample-mobile-app-ios
git submodule init
git submodule update
cd incubator-milagro-mfa-sdk-ios
git submodule init
git submodule update

The InAppLoginSampleApp/MPin.xcworkspace file can then be opened in XCode in order to build the project.

Create a demo web app to act as a backend service

Now that the project is imported, in order to enable you to test the demo iOS app, you should first of all create a demo MFA web app using one of our web SDKs, as explained in the SDK Instructions section of this documentation. The purpose of this is to have a running service which can authenticate all users who are registering with and logging into your iOS app (i.e. the web app will not be used for the purpose of in-browser login). Please note that, currently, only the dotnet SDK supports this functionality.

Once you have used the authentication portal to obtain a Client Id and Secret for your demo web app, you will need to configure the demo web app with these values, using the instructions provided on the web SDK page. It will then need to be hosted on an available url which means it is accessible by the iOS app. These steps are illustrated on the web SDK instructions page. A further note is that, if you are setting this up on a simple private network and using IIS Express to run the app which has been configured in Visual Studio, it will be necessary to make sure the firewall of the host machine allows incoming connections to the relevant port, and that the .vs/config/applicationhost.config file contains bindings which make it available:

    <binding protocol="http" bindingInformation="*:5000:" />
    <binding protocol="http" bindingInformation="*:5000:" />

The <binding protocol="http" bindingInformation="*:5000:" /> line will allow binding to any IP. Using, for example, <binding protocol="http" bindingInformation="*:5000:" /> would allow binding to a specific private IP only.

Note that here is only being used as an example of a private IP. ipconfig or ifconfig should be used to determine the private IP of the host machine.

Note that, for the web app settings in the authentication portal you will need to make sure there is a redirect url which has the private IP of the host machine as its base:

Once the demo web app is running and hosted it will be available for the iOS app to connect to as the backend service to which your iOS app can authenticate.

Add company id to the project and test the app

Before building an iOS app, you will need to obtain your company id as the owner of the MFA web app. This is visible as a tooltip in the top right corner of your company dashboard in the portal:

view cid

Once you have the company_id open src/RegisterViewController.m and edit the line:

NSString *kStrCID = @"<company-id-goes-here>";

Then also let the app know the configured private url/IP by editing the line around 176:

NSURL *theUrl = [NSURL URLWithString:@""];

Save and then open src/PinPadViewController.m to add the configured private url/IP around line 175:

NSURL *theUrl = [NSURL URLWithString:@""];

Again, note that here is only being used as an example of a private IP. ipconfig or ifconfig should be used to determine the private IP of the host machine.

You can now build and run the XCode simulator and test the app by registering a new identity, creating a pin and logging in.

iOS Demo App Overview

The following tree view illustrates the file structure of the demo application found in the InAppLoginSampleApp subfolder:


The main storyboard view SampleApp/MPinApp/MPinApp/Base.lproj/Main.storyboard shows the configuration of the different views and associated messages:



This file enables user registration and is responsible for initializing the SDK.

AS explained above, the first task here is to pass the Company ID retrieved from the portal (explained above) as *kStrCID:

NSString *kStrCID = @"ea1___13-***-****-****-43b2____9274";

The Company ID is set with SetClientId, and the relevant domains are added as trusted:

[MPinMFA SetClientId:kStrCID];
[MPinMFA AddTrustedDomain:@""];
[MPinMFA AddTrustedDomain:@""];
[MPinMFA AddTrustedDomain:@""];

Users Array

Within 'viewWillAppear' an array is set up to check if there are any registered users with the service:

NSArray *arrUsers = [MPinMFA listUsers];

If arrUsers.count returns 0, then setupAddId will call the AddId view to begin the user registration process:

add id

The demo app is only configured to deal with one registered user, so if arrUsers.count returns > 1 user, the SDK DeleteUser method is called to delete all users. setupAddId will call the AddId view to begin the user registration process.

If the result is that there is one user found in the array (_user = arrUsers[0];) then the SDK getState method is called:

  • If the user state is INVALID, the DeleteUser method is called and setupAddId will call viewAddId to begin the user registration process.
  • If the user state is REGISTERED, setupRegistered calls viewRegistered and makes use of the getState, getBackend, getIdentity and GetCustomerId methods to display the relevant info for the registered customer, who can login:

view registered

  • If the user state is STARTED_REGISTRATION, setupStartedRegistration calls viewStartedRegistration and makes use of the getState, getBackend, getIdentity and GetCustomerId methods to display the relevant info for the customer, who can then complete the registration process:

started reg

The getAccessCode method

Within the getAccessCode method it can be seen that the app makes use of the /authzurl API endpoint to begin the process of retrieving an access code:

NSURL *theUrl = [NSURL URLWithString:@"https://<backendserviceip>/authzurl"];

Ultimately this returns an authorization url as a json data object which is passed to the accessCodeReaded method:

[self accessCodeReaded:jsonData];

The accessCodeReaded method

This uses the authorization url to retrieve the access code:

MpinStatus *mpinStatus  = [MPinMFA GetAccessCode:jsonObject[@"authorizeURL"] accessCode:&strAccessCode];

If this is successful an access code is returned:

case OK:
    NSLog(@"%@", strAccessCode);
    _accessCode  = strAccessCode;

The access code can then be used by the addUser and startAuthentication methods.

The addUser method

This method uses the MakeNewUser SDK method which passes the _txtAddUser variable (the user's email address):

_user = [MPinMFA MakeNewUser:_txtAddUser.text deviceName:@"SampleDevName"];

As will be seen later the user's email (_txtAddUser) is retrieved from - (IBAction)addID:(id)sender.

The StartRegistration SDK method is then used, which passes the user and access code:

MpinStatus *mpinStatus = [MPinMFA StartRegistration:_user

The addUser method is put to use with the - (IBAction)addID:(id)sender action / button, which starts the registration process.

The startAuthentication method

Once a user has been registered this method can be used to begin the actual authentication process for logging into the app. This is done with the startAuthentication SDK method:

MpinStatus *mpinStatus = [MPinMFA StartAuthentication:_user accessCode:_accessCode];

If the status is OK the PinPadViewController is called:

PinPadViewController *vc = [[UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateViewControllerWithIdentifier:@"PinPadViewController"];
vc.accessCode = _accessCode;
[self.navigationController pushViewController:vc animated:YES];

Once the use has been registered, the *startAuthentication method is put to use with the - ( IBAction) login:(id)sender** action / button (i.e. when the user clicks the 'login' button)

The confirmRegistration method

This method checks whether the user has confirmed their identity with the activation link that is send to their mail inbox. If so then the app navigates them to the set up pin page (PinPadViewController). If not they are prompted to do so with an on-screen message shown for 3 seconds:

It uses the ConfirmRegistration SDK method:

MpinStatus *mpinStatus = [MPinMFA ConfirmRegistration:_user];

The confirmRegistration method is put to use in the - (IBAction)confirmEmail:(id)sender action / button.

The setBackend method

Found at the end of the file, this method uses the SDK SetBackend method sets the issuer URL for the API service. It should be left as is:

MpinStatus *mpinStatus = [MPinMFA SetBackend:@""];

The 'Resend Email' button

In - (IBAction)resendEmail:(id)sender it can be seen that this action actually deletes the user and re-starts the registration process:

- (IBAction)resendEmail:(id)sender

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ {
        NSString *strUserID = [self.user getIdentity];
        [MPinMFA DeleteUser:self.user];

        self.user = [MPinMFA MakeNewUser:strUserID deviceName:@"SampleDevName"];
        MpinStatus *mpinStatus = [MPinMFA StartRegistration:self.user

        dispatch_async(dispatch_get_main_queue(), ^ (void) {
            NSString * msg = ( mpinStatus.status == OK ) ? ( @"The Email has been resent" ) :
                                                            ([NSString stringWithFormat:@"An error has occurred! Info - %@", [mpinStatus getStatusCodeAsString]] );
            [[[UIAlertView alloc] initWithTitle:@"Info" message:msg delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil] show];


enter pin

Once called the user can use this view to first of all create their PIN. The PIN can then be used to login at any time.

When the user enters their PIN and clicks 'send' (- (IBAction)onClickSendButton:(id)sender), a user status check is made. If the status is STARTED_REGISTRATION the FinishRegistration SDK method is used:

MpinStatus *mpinStatus = [MPinMFA FinishRegistration:_user pin:_txtPinPad.text];

and the app navigates to the SuccessfulViewController view:

if ( mpinStatus.status == OK )  {
    UIViewController *vc = [[UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateViewControllerWithIdentifier:@"SuccessfulViewController"];
    [self.navigationController pushViewController:vc animated:YES];

If the user status is REGISTERED the FinishAuthentication SDK method is used:

MpinStatus *mpinStatus = [MPinMFA FinishAuthentication:_user

If the FinishAuthentication method returns a status of OK the checkAuthenticationStatus method is called.

The checkAuthenticationStatus method

This method makes use of the retreived strAuthzCode to obtain an authorization token from the /authtoken API endpoint from the backednd service url:

NSURL *theUrl = [NSURL URLWithString:@""];

Again, note that here is only being used as an example of a private IP. ipconfig or ifconfig should be used to determine the private IP of the host machine.

The getIdentity SDK method is also put to use here.

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:theUrl cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:10];
[request setTimeoutInterval:10];
request.HTTPMethod = @"POST";
NSString *str = [NSString stringWithFormat:@"{ \"code\": \"%@\",\"userID\": \"%@\" }",strAuthzCode, [_user getIdentity]];

NSHTTPURLResponse *response;
NSError *error;

request.HTTPBody = [str dataUsingEncoding:NSUTF8StringEncoding];

NSData *dataResponse = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];

If the request is successful the user is logged in:

    UIAlertController * alert=   [UIAlertController alertControllerWithTitle:@"Congrats"
                                                                     message:@"You have been logged in successfully"
    UIAlertAction* yesButton = [UIAlertAction actionWithTitle:@"Start over"
                                                      handler:^(UIAlertAction * action){
                                                          [self.navigationController popToRootViewControllerAnimated:YES];
    [alert addAction:yesButton];
    alert.view.alpha = 1.0;
    alert.view.backgroundColor = [UIColor whiteColor];
    alert.view.layer.cornerRadius = 8.0;
    alert.view.clipsToBounds = YES;
    [self presentViewController:alert animated:YES completion:nil];