NAV Navbar

Introduction

The Notakey Xamarin SDK is a Xamarin library designed to allow development of Xamarin mobile applications.

It can be downloaded from NuGet.

This SDK requires Notakey Authentication Servers with Enterprise licencing.

Technical Summary

Notakey.Xamarin.Sdk supports iOS and Android Xamarin application development. It requires .NETPortable 4.5 or newer.

Notakey SDK supports Notakey service discovery, user device onboarding to Notakey Authentication Server and interaction with Notakey authorization messages.

Installation Instructions for NuGet

Add Notakey.Xamarin.Sdk NuGet package to related mobile application projects.

Glossary

Term Description
Service Domain Endpoint of Notakey Authentication Server that holds one or more applications with defined users. In Xamarin SDK context it is used to store endpoint base URL (for example: https://demo.notakey.com)
Application or service Both terms are used interchangeably. This document uses the term Service in order to avoid confusion between mobile application and Notakey Authentication Server application. In source code, Application is used to describe a Notakey Authentication Server application. A Notakey Application acts as a communication node between onboarded user devices, messaging services and third party services to which it is being used to authorize (for example: VPN). In SDK context application object stores information about unique application access ID, onboarding requirements and information to display to user (application name, logo)
Onboarding / offboarding Process to register or remove user device to Notakey Authentication Server application. In onboarding process cryptographic keys are generated in user device, user performs one or more authentication steps and on successful completion key fingerprint and user information is sent and registered in Notakey Notakey Authentication Server application. Note that cryptographic keys never leaves device secure enclave.
Authorization request Authorization request is received either by push notification if mobile application is registered to receive them (and Notakey Authentication Server - to send) or by manually obtaining them directly from Notakey Authentication Server. It contains information about source application that requires authorization, request and expiration time, and other metadata.
Notification token Unique identifier for device that is obtained from Apple or Google when application is registered to receive push notifications. To receive push notifications from Notakey Authentication Server mobile application should be registered and have valid push notification token that is also sent to Notakey Authentication Server. Note that Notakey Authentication Server also has to be configured with valid messaging service. But that is outside of mobile application development scope.
Cryptographic key pair Cryptographic key pair is created in user device secure enclave, never leaves it, and is used to unique identify user device when it is onboarded and to sign authorization request responses.

Quickstart

Step Overview

In order to achieve the desired end goal - for user to be able to receive authorization requests and respond to to them, necessary consecutive steps are described in following sections.

In a nutshell mobile application should:

  • Initialize Notakey Xamarin SDK. In initialization process all necessary background workers are created and registered.
    • IErrorLogger interface implementation should be created and passed to Initializer. The error logger can be useful to remotely aggregate application events and errors.
    • Working implementation of INotificationTokenAcquirer interface should be created and passed to Initializer, to be able to send actual token to Notakey Authentication Server and to receive push notifications.
  • Valid and reachable Notakey Authentication Server service domain should be added to mobile application domain repository. In process service domain services will be updated and added to mobile application service repository.
    • Alternatively, service can be added directly, without adding service domain and without pulling it’s services list. In that case some of the functionality will be lost (for example: service refresh).
  • Optionally. Mobile application should be registered to receive push notifications (follow Apple and Google documentation for how to achieve it).
  • Cryptographic keys should be generated for service in user device for onboarding process. Simplest way is to follow onboarding process quickstart guide where keys are generated automatically.
  • User interface for user device onboarding should be created.
  • User interface for authorization requests should be created.
  • Mechanism for manual request renewal should be implemented.
  • Response mechanism to authorization requests should be implemented.
  • Optionally. Push notification listening service should be implemented (follow Apple and Google documentation for how to achieve it).

Initialization

Notakey uses Simple Injector to set up most of necessary instances and necessary interface implementations. Notakey SDK should be initialized only once per application on application start-up. Typical initialization location for iOS is AppDelegate in FinishedLaunching, and for Android in MainActivity OnCreate or GcmListenerService implementation.

Most of necessary underlying objects are initialized as singletons and should not be created separately. They can be accessed through DependencyInitializer.

DependencyInitializer

Notakey.Infrastructure.DependencyInitializer

// Example of Initialize call
Container container = DependencyInitializer.InitializeOnCurrentThread(
  InitializeLocation.AndroidActivity,
  new DebugErrorLogger(new LoggerMessageFormatter()),
  new DummyNotificationTokenAcquirer(),
  new DummyOnboardingTranslationStrings());
Type Property Description
Container Container Provides access to related Notakey objects
HashSet InitializeLocation List of active initialization locations
Type Method Description
Container InitializeOnCurrentThread(InitializeLocation location, IErrorLogger logger, INotificationTokenAcquirer tokenAcquirer, IOnboardingTranslationStrings onboardingTranslationStrings) Initialize underlying Notakey SDK objects

Loading Instances

Notakey object instances can be accessed through DependencyInitializer Container property.

// Example of accessing instances
var container = DependencyInitializer.Container;
var errorLogger = container.GetInstance<IErrorLogger>();
var applicationFinderStatus = container.GetInstance<ISubject<ApplicationsFinderStatus>>();

InitializeLocation

Notakey.Core.InitializeLocation

InitializeLocation values are descriptive and only meant for reference. They does not affect any underlying functionality.

Initialize Location
AppDelegate
AndroidActivity
AndroidService

IErrorLogger

Notakey.Infrastructure.IErrorLogger

Method
void RegisterUnhandledExceptionHandler()
Task Capture(Exception e, IDictionary tags = null, IDictionary extra = null, [CallerFilePath] string filePath = “”, [CallerMemberName] string memberName = “”, [CallerLineNumber] int lineNumber = 0)
void Debug(string message, [CallerFilePath] string filePath = “”, [CallerMemberName] string memberName = “”, [CallerLineNumber] int lineNumber = 0)

Provides ability to capture debug and error messages throughout Notakey SDK and send them to preferred targets, including remote aggregators.

Simple implementation DebugErrorLogger of IErrorLogger is provided and forwards messages to System.diagnostics.Debug output.

INotificationTokenAcquirer

Notakey.Services.INotificationTokenAcquirer

NotificationTokenAcquirer is used to communicate acquired iOS and Android notification token through out the application. It is used as a source for latest notification token value when onboarding a service.

Type Property
IObservable Token
// Example of dummy notification token implementation
public class DummyNotificationTokenAcquirer : INotificationTokenAcquirer
    {
        public IObservable<string> Token => _staticToken;
        static IObservable<string> _staticToken;
    public DummyNotificationTokenAcquirer()
    {
      _staticToken = Observable.Return("dummy_token").Repeat();
    }
  }

IOnboardingTranslationStrings

Notakey.Services.IOnboardingTranslationStrings

IOnboardingTranslationStrings is used to get related text values for provided OnboardingPage UI.

Type Property
string AlertTitle_Information
string AlertTitle_Success
string AlertTitle_Failure
string AlertButton_Ok
string AlertButton_Cancel
string OnboardingPage_OnboardingSucceededMessage
string OnboardingPage_OnboardingFailedMessage
string OnboardingPage_OnboardingTimeoutMessage
string OnboardingPage_OnboardingNoRequirementsMessage
string OnboardingPage_ApplicationAlreadyOnboardedMessage
string Keychain_Capabilities_Reason
string Keychain_Passcode_Reason
string WebviewProofCreatorPage_LoadingTitle
string OnboardingPage_Title

Basic implementation DummyOnboardingTranslationStrings of IOnboardingTranslationStrings is provided to get default translation string values in English.

Domains and Domain Services

Note: Many of actions associated with services can be performed working directly to IApplicationRepository and use of domain entities in most cases are not mandatory (but part of functionality could be lost).

Service Domain Management

Added service domains are stored locally in SQLite and is accessed through IDomainRepository

Adding New Service Domains

New service domains to IDomainRepository can be added either by adding ServiceDomain objects directly to it or by adding service domain URL do DomainApplicationLoader. In case of the second option, the added service domain will be validated (it should be reachable) and related services will also be added to IApplicationRepository.

// Add new Notakey Authentication Server service domain to domain repository.
// If non-standard port is used, it should be provided in URL.
// Found services for added service domain will be added to local
// service repository.
var domainToAdd = "http://demo.notakey.com";
var domainLoader = container.GetInstance<DomainApplicationLoader>();
var domainRepository = container.GetInstance<IDomainRepository>();

if (!domainRepository.Contains(domainToAdd))
{
    domainLoader.AddDomain(domainToAdd);
}

Removing Existing Service Domain

// remove local service domain.  Note that related services from
// repository should be removed separately.
var serviceDomain = new ServiceDomain(domain);
var domainRepository = container.GetInstance<IDomainRepository>();
domainRepository.Remove(serviceDomain);

Service Management

Added services are stored locally in SQLite, and are accessed through IApplicationRepository. Each service is uniquely identified by its Access ID.

Refreshing Services

By performing service refresh all reachable service domains in IDomainRepository will be checked for application changes. If needed local service information will be updated and unreachable services will be removed from local IApplicationRepository.

// Service list refresh for added services in IDomainRepository
var appRefreshService = container.GetInstance<ApplicationRefreshService>();
appRefreshService.FindApplications();

Removing Existing Service

Note that service information will be removed locally and if it still exists in remote authentication server domain, service will be added again on next refresh.

// Removing service from local repository
var qualifiedApplication = new QualifiedApplicationDescriptor();
var appRepository = container.GetInstance<IApplicationRepository>();
appRepository.Remove(qualifiedApplication);

Service Onboarded Status

Services are pinned automatically in case of successful onboarding.

//Check if Service is onboarded locally.
//Note that remotely user device or whole service still can be removed.
var qualifiedApplication = new QualifiedApplicationDescriptor();
var appRepository = container.GetInstance<IApplicationRepository>();
var pinningService = container.GetInstance<IApplicationPinningService>();
pinningService.IsPinned(qualifiedApplication);

Onboarding

Note that mobile device screen lock pin (or password) should be set and activated to perform onboarding. It is mandatory requirement to access mobile device secure enclave and encryption keys.

OnboardingPage

Notakey.Infrastrcture.OnboardingPage

User device onboarding to service can be performed using OnboardingPage provided in Notakey SDK. OnboardingPage provides necessary custom renderers for iOS and Android platforms for supported onboarding requirements.

// Service onboarding example
async void BeginOnboardingAsync(QualifiedApplicationDescriptor app,
    IOnboardingService onboardingService, IApplicationPinningService pinningService,
    IApiFactory apiFactory, IKeychain keychain, IKeyValueStore keyValueStore,
    NotificationTokenUpdater notificationTokenUpdater,
    INotificationTokenAcquirer notificationTokenAcquirer, WarningMessageService warningService,
    IPlatformSpecific platformSpecific, IOnboardingTranslationStrings onboardingTranslationStrings)
{
  var page = new OnboardingPage(app, onboardingService, pinningService, apiFactory,
              keychain, keyValueStore, notificationTokenUpdater, notificationTokenAcquirer,
              warningService, platformSpecific, onboardingTranslationStrings);

  await Navigation.PushAsync(page);
  page.StartOnboarding();
}

Offboarding

User device offboarding from service can be performed using IOnboardingService. Note that offboarded services are not automatically removed from IPinStatusRepository and it should be done manually on successful offboarding.

// Example of service offboarding
void PerformOffboarding(QualifiedApplicationDescriptor app)
{
  var onboardingService = container.getInstance<IOnboardingService>();
  onboardingService
      .UnboardIfExists(app, app)
      .DefaultIfEmpty(null)
      .SingleAsync()
      .Subscribe((userDevice) =>
            {
              pinningService.UnpinIfPinned(app);
            }, (exc) => { });
}

Notification Token Update

To receive push notifications from Notakey Authentication Server, actual, active push notification token should be provided to it for onboarded devices. It is done using Notakey.Services.NotificationTokenUpdater

// Example of notification token update
void UpdateNotificationTokenForServices(NotificationTokenUpdater tokenUpdater,
  IEnumerable<QualifiedApplicationDescriptor> serviceList,
  string token)
{
  tokenUpdater
    .UpdateTokenForApplications(serviceList, token)
    .Subscribe((obj) => { }, (exc) => { });
}

Service Authorization Requests

Authorization service requests can be obtained either by receiving push notifications from messaging service or by manually refreshing them from Notakey Authentication Server.

Authorization Request Refresh

Notakey.Services.IRequestService

To refresh authorization requests from the Notakey Authentication Server, it is not necessary for push notifications to be configured.

They are pulled directly from the authentication server using its API.

// Service authorization requests refresh example
void RefreshAuthRequests(IRequestService requestService)
{
    requestService.RefreshRequests();
}

To access refresh requests same IRequestService should be used.

// Accessing service authorization requests
ObservableCollection<ApplicationRequestDescriptor> authRequests = requestService.AuthRequests;

Push Notifications

Notakey Messenger sends notifications of two categories. - NewAuthRequest New authorization request notification. - InvalidatedAuthRequest Notification that indicates, that authorization request is invalidated. Usually this type of notification is sent when authorization request is approved or denied from another user device.

In case of InvalidatedAuthRequest it’s uuid is not unique to specific request. Instead it is same as uuid of authorization request it invalidates.

Push Notification JSON Example

Note that “aps” object is sent only to iOS devices and root “category” property is sent only to Android devices.

{
    "auth_request": {
    "uuid": "notification_uuid_value",
    "description": "Online login",
    "action": "Web login",
    "expires_at": "1970-01-01 00:00:05 +0300",
    "access_id": "service_access_id_value",
    "type": "AUTH",
    "title": "Service Title",
    "created_at": "1970-01-01 00:00:00 +0300",
    "expired": "false",
    "request_ip": "127.0.0.1",
    "logo_url": "/_cimg/logo_id?size=132x132",
    "logo_sash_url": "/_cimg/logo_sash_id?size=369x60"
    },
    "__client_metadata__": {
        "messenger_version": "v1",
        "title": "ServiceTitle",
        "description": "Online login",
        "category": "NewAuthRequest"
    },

    "aps": {
        "sound": "",
        "category": "NewAuthRequest",
        "alert": "Online login"
    },

    "category": "NewAuthRequest"
}

RequestDescriptor from Push Notification

Notakey.Core.RequestDescriptor is created from received push notification “auth_request” object and holds its properties. They are needed to send response to authorization request.

Responding to Authorization Requests

Using IRequestResponder

// Sending response to authorization request
void SendResponse(IRequestResponder requestResponder,
  QualifiedApplicationDescriptor app,
  ResponseDescriptor response)
{
  requestResponder.SignAndSendPayload(app, response)
      .SingleAsync()
      .Subscribe( (obj) => {}, exc => {});
}

Building and Sending the Response

// Sending response to authorization request
void CreateAndSendResponse (QualifiedApplicationDescriptor app,
  ResponseDescriptor response, IResponseSigner responseSigner,
  IApiFactory apiFactory, IKeychain keychain)
{
  // Service keypair is created in onboarding process
  var keypair = keychain.GetKeypair(app, false);

  var signedMessage = responseSigner.CreatePayload(response, keypair);

  var api = apiFactory.CreateAuthRequestApi(app, response.Request);

  var payload_base64 = Convert.ToBase64String(signedMessage.Payload);
  var payload_type = Convert.ToString(signedMessage.SignedResponse.Signature.Type);
  var response_type = Convert.ToString(response.Type);

  api.SendResponse(response.Request.Uuid, payload_base64, response_type, payload_type)
    .SingleAsync()
    .Subscribe( (obj) => {}, exc => {});
}

Key Management

Notakey.Core.IKeychain

User device keypair is used in order to uniquely identify user device to Notakey Authentication Server and to sign authorization requests. New keypair is created in device secure element for each service when user device is onboarded.

Note that Notakey uses cryptographic keys on mobile devices that are protected by device pin/password and are deleted when it is removed.

To access user device cryptographic keys using IKeychain interface, object implementing Notakey.Core.IKeypairOwner interface is required. [Qualified]ApplicationDescriptor implements this interface.

Notakey.Core.IKeypairOwner

Type Property
KeyRequirements KeyRequirements
string KeyAlias

By default, on creation ApplicationDescriptor KeyRequirements is set to use RSA encryption algorithm and requires key encryption with device password.

Key alias is composed from service access ID and key requirements.

Creating and Accessing Keypair

// Accessing (and generating) user device keys
void INNKeypair GetKeypair(IKeychain keychain, IKeypairOwner keypairOwner, bool generateIfMissing)
{
  var keypair = keychain.GetKeypair(keypairOwner, generateIfMissing);
  return keypair;
}