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 |
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;
}