RapID QR code authentication

You can add a QR code login on your RapID enabled website. Your registered end-users scan the QR code with the app that you've provided and are securely logged in with two way authentication of their RapID credentials.


Sample RapID QR code

How it works

A random authentication code is generated and stored in your web server. Your website's login page renders that authentication code as a RapID QR code and listens for an authenticated event from the website. When your app scans the QR code, it reads the authentication code and POSTs that as JSON to an authentication URL on your website, along with the end-user's RapID credential. Your website uses the supplied credential to identify and validate the end-user, and marks the authentication code stored on your web server as authenticated. This causes the authenticated event to be fired and the end-user is redirected to your website's secure area.

How to implement it

You could of course develop this all for yourself, but to help you out, you can download our .NET Rapid.Authentication SDK which takes care of a lot of the work for you. You will need to integrate this SDK into your website and develop a QR code scanner feature in your mobile app.

The exact steps to implement this depend on the technologies used by your website and also the mobile operating system or systems that you support. The sections below show how to do this for some scenarios.

If the technologies you are using are not covered please contact rapid@intercede.com.

RapID QR code authentication in your website

Client certificate validation

If you are using the RapID library, then you must provide a class which implements the IValidateCertificate interface (namespace Rapid.Authentication.Authentication). When your end-users scan the QR code and POST an authentication request to your web server's URL, the RapID library will call the IsValidCertificate method that you provide.

In some hosting environments it is not possible to deploy your RapID trusted issuer certificate in such a way that it would provide two way TLS validation of clients and thus prevent unauthorised access to your web server's routes. E.g. IIS manages trust check when it is running under full control of the host, but on shared systems such as Azure Web Apps, IIS is constrained and cannot do this.

If your hosting environment supports full two way validation then your IsValidCertificate method can simply return true. For other hosting environments such as Azure Web Apps, you must provide code to explicitly validate client certificates. Your method needs to validate that the certificate was issued by your RapID trusted issuer certificate.

The following example shows how you can validate certificates where the trusted issuer certificate is deployed to the file system of your web app.

using System;
using System.Security.Cryptography.X509Certificates;
using Rapid.Authentication.Authentication;
using Serilog;

public class CertificateValidator : IValidateCertificate
{
    public bool IsValidClientCertificate(X509Certificate2 clientCertificate)
    {
        var chain = new X509Chain();
        var trustedIssuerCertificate =
            new X509Certificate2(
                $@"{AppDomain.CurrentDomain.BaseDirectory
                    }\bin\RapID\Rapid_Your_Company_Name_TrustedCa.cer",
                string.Empty);

        Log.Logger.Information($"trustedIssuerCertificate = {trustedIssuerCertificate}");

        chain.ChainPolicy.ExtraStore.Add(trustedIssuerCertificate);
        chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
        chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
        var x509ChainBuiltOkay = chain.Build(clientCertificate);

        Log.Logger.Information($"x509ChainBuiltOkay={x509ChainBuiltOkay}");
        Log.Logger.Information($"chain.ChainElements.Count={chain.ChainElements.Count}");

        var isValid =
            x509ChainBuiltOkay &&
            chain.ChainElements.Count == 2 &&
            chain.ChainElements[1].Certificate.Thumbprint == trustedIssuerCertificate.Thumbprint &&
            chain.ChainElements[0].Certificate.Thumbprint == clientCertificate.Thumbprint;

        return isValid;
    }
}

Note: ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority is required because the .cer file is not in the Trusted Root Certification Authorities certificate store. It is important to not simply rely on the status from X509Chain.Build since that would return true if the client certificate is valid, but not necessarily issued by your trusted issuer certificate.

IdentityServer3

The Rapid.Authentication SDK can be integrated into an IdentityServer3 solution, meaning that as well as the standard username/password login form, you can have a RapID QR code which end-users scan.

The notes here assume that you already have an IdentityServer3 solution which is providing local login with username and password. As such, it is assumed that you already have a custom user service, though we do not assume that you have a custom view service.

There is a wealth of online documentation for IdentityServer3 as well as sample code. The following links are of particular relevance to what is discussed here.

Custom User Service documentation
Custom View Service documentation
GitHub IdentityServer3 samples

Step 1. Add a reference to the Rapid.Authentication assembly

The Rapid.Authentication SDK that you downloaded includes a NuGet package for the Rapid.Authentication assembly. Extract this locally and install it into your identity server solution from the Visual Studio package manager console.

PM> install-package C:\Temp\Rapid.Authentication.n.n.n.nupkg 

Step 2. Create an Identity Server logon store

As described above, each time a client browses to your logon page, a random authentication code is generated and stored in your web server. Such authentication (or challenge) codes are encapsulated by the AuthenticationRequest class (namespace Rapid.Authentication). For IdentityServer3, we need something to uniquely identify each logon request; the redirect URL meets this need.

To support systems such as IdentityServer3 which have a two-phase logon process, we provide a specialised version of the AuthenticationRequest class: TwoPhaseAuthenticationRequest (namespace Rapid.Authentication) into which you can store the redirect URL. The RapID Library will later set up the AnonymousUserId.

Your identity server needs to store a TwoPhaseAuthenticationRequest object for each rendered logon page. When an end-user scans the QR code and POSTs to the authentication URL, the RapID library will locate the TwoPhaseAuthenticationRequest via the TwoPhaseAuthenticationKey (redirect URL) and set the AnonymousUserId using the supplied end-user RapID credential.

To hook this up, your TwoPhaseAuthenticationRequest object store must derive from FindAuthenticationRequest<TwoPhaseAuthenticationRequest> and must implement the interface INewAuthenticationRequest<TwoPhaseAuthenticationRequest> (namespace Rapid.Authenticate.Persistence). In most cases this store will need to be persisted so that it is sharable by multiple instances of your identity server. For example; to support load-balancing; or to facilitate both two way and one way TLS routes if deploying as Azure web services. However, for clarity, here is an example in-memory implementation.

using Rapid.Authentication; 
using Rapid.Authentication.Persistence; 

public class TwoPhaseAuthenticationRequestStore  
            : FindAuthenticationRequest<TwoPhaseAuthenticationRequest> 
            , INewAuthenticationRequest<TwoPhaseAuthenticationRequest> 
{ 
    protected readonly List<TwoPhaseAuthenticationRequest> Requests =  
                    new List<TwoPhaseAuthenticationRequest>(); 

    public void Add(TwoPhaseAuthenticationRequest request) 
    { 
        Requests.Add(request);
    } 

    public bool HasAuthenticated(string twoPhaseKey) 
    { 
        return AuthenticatedUserId(twoPhaseKey) != null; 
    } 

    public string AuthenticatedUserId(string twoPhaseKey) 
    { 
        return FindByTwoPhaseKey(twoPhaseKey)?.AnonymousUserId; 
    } 

    public override TwoPhaseAuthenticationRequest FindByChallenge(string challenge) 
    { 
        return Requests.SingleOrDefault(x => x.Challenge == challenge); 
    } 

    private TwoPhaseAuthenticationRequest FindByTwoPhaseKey(string key) 
    { 
        return Requests.SingleOrDefault(l => l.TwoPhaseAuthenticationKey == key); 
    } 
} 

For further details about implementing a persistent store see Persistent storage.

Step 3. Set up a RapID aware user service

We are assuming that your identity server already has a custom user service. You will need to make some changes to support RapID QR authentication. For clarity we will again use an in-memory implementation here, though again it is highly likely that your solution will need to make use of a persistent user store that is shared by multiple instances of your identity server.

You need to make your custom user service be RapID-aware and then override the IsActiveAsync, and PreAuthenticateAsync methods so that they handle RapID authentication.

The constructor for your User Service is likely to already take a collection of users. Additionally, it must take the TwoPhaseAuthenticationRequestStore which you implemented in Step 2.

public class InMemoryRapidUserService : InMemoryUserService 
{ 
    private readonly List<InMemoryUser> users; 
    private readonly TwoPhaseAuthenticationRequestStore rapid; 

    public InMemoryRapidUserService( 
                List<InMemoryUser> users,  
                TwoPhaseAuthenticationRequestStore rapid) 
        : base(users) 
    { 
        this.users = users; 
        this.rapid = rapid; 
    } 

Override PreAuthenticateAsync, which is called before the Login page is shown.
The SignInMessage.ReturnUrl of the supplied context is the unique identity for this IdentityServer3 logon. This function should examine your authenticationRequestStore to see if the particular return URL has authenticated. If it has, then it should return an AuthenticateResult containing the end-user's anonymous ID and username.

public override Task PreAuthenticateAsync(PreAuthenticationContext context) 
{ 
    if (rapid.HasAuthenticated(context.SignInMessage.ReturnUrl))
    {
        var rapidId = rapid.AuthenticatedUserId(context.SignInMessage.ReturnUrl);
        context.AuthenticateResult = AuthenticateResultForUser(rapidId);
    }

    return Task.FromResult(0);
} 

private AuthenticateResult AuthenticateResultForUser(PreAuthenticationContext context, string rapidId) 
{ 
    var user = FindAuthenticatedUser(rapidId); 

    if (user == null) return null; 

    return new AuthenticateResult(user.Subject, user.Username);; 
} 

private InMemoryUser FindAuthenticatedUser(string anonId) 
{ 
    return anonId == null ? null : users.SingleOrDefault(u => u.ProviderId == anonId); 
} 

Override IsActiveAsync, which is called whenever identity server needs to determine if the user is still considered valid or active. Set IsActive to true if the context contains a subject Id, which will be the case when the user has logged in.

public override Task IsActiveAsync(IsActiveContext context) 
{ 
    var subjectId = context.Subject.GetSubjectId(); 
    if (!string.IsNullOrEmpty(subjectId)) 
    { 
        context.IsActive = true; 
    } 

    return Task.FromResult(0); 
} 

Note: In the above example, we have used the IdentityServer3 InMemoryUser class and made use of the ProviderId property to hold the anonymous ID. So making use of the test certificate requests that were created for you when you signed up to RapID we may have a collection of users as follows:

public static class Users 
{ 
    public static List<InMemoryUser> Get() 
    { 
        return new List<InMemoryUser> 
        { 
            new InMemoryUser 
            { 
                Username = "bob", 
                Password = "secret", 
                Subject = "Bob Smith", 
                ProviderId = "TestCertificateRequestValid1", 

                Claims = new[] 
                { 
                    new Claim(Constants.ClaimTypes.GivenName, "Bob"), 
                    new Claim(Constants.ClaimTypes.FamilyName, "Smith"), 
                    new Claim(Constants.ClaimTypes.Role, "Supervisor"), 
                    new Claim(Constants.ClaimTypes.Role, "Worker") 
                } 
            }, 
            new InMemoryUser 
            { 
                Username = "jim", 
                Password = "secretjim", 
                Subject = "Jim Jones", 
                ProviderId = "TestCertificateRequestValid2", 

                Claims = new[] 
                { 
                    new Claim(Constants.ClaimTypes.GivenName, "Jim"), 
                    new Claim(Constants.ClaimTypes.FamilyName, "Jones"), 
                    new Claim(Constants.ClaimTypes.Role, "Worker"), 
                } 
            } 
        }; 
    } 
} 

Step 4. Add the RapID QR code to the login page

To add the QR code to the identity server login page, you need to create a RapID aware view service. The examples here are based on the IdentityServer3 sample DefaultViewService.sln.

A custom view service allows you to override the default login pages that IdentityServer3 provides. By using a partial view, we are able to insert the HTML to render the RapID QR Code in the login page, and by deriving a custom view service from DefaultViewService we are able to supply custom data to that html via the view model.

You need to add a _login.html file to the templates folder of your identity server solution. Then, after the Local Login <div/>, include a new <div/> for the RapID QR code image (model.custom.rapidUrls.qrImageUrl) and scan event handler (challenge_event_url which uses model.custom.rapidUrls.eventsUrl).

...
 <div class="row"> 
    <div class="col-md-6 col-sm-6" ng-show="model.loginUrl"> 
        ...
    </div> 

    <div class="col-md-6 col-sm-6" ng-show="model.loginUrl"> 
        <div class="panel panel-default"> 
            <div class="panel-heading"> 
                <h3 class="panel-title">RapID Login</h3> 
            </div> 
            <div class="panel-body"> 
                <input type="hidden" id="challenge_event_url" value="{{model.custom.rapidUrls.eventsUrl}}"/> 

                <form name="form" method="post" action="{{model.loginUrl}}"> 
                    <anti-forgery-token token="model.antiForgery"></anti-forgery-token> 

                    <fieldset> 
                        <div class="qr-code"> 
                            <img id="logon_bitmap" title="Scan QR code to logon." src="{{model.custom.rapidUrls.qrImageUrl}}" alt="Logon barcode"/> 
                        </div> 
                        <!--<div> 
                            {{model.custom.code}} 
                        </div>--> 
                        <p class="qr-description"> 
                            Login with your phone 
                        </p> 
                    </fieldset> 
                </form> 
            </div> 
            <ul class="list-unstyled"> 
                <li ng-repeat="link in model.additionalLinks"><a ng-href="{{link.href}}">{{link.text}}</a></li> 
            </ul> 
        </div> 
    </div> 
    ...
  </div> 
  ...

You also need to create a view service, derived form DefaultViewService and override the Login method to include custom data in the model that is supplied to the html form shown above.

public class RapidViewService : DefaultViewService 
{ 
    private readonly TwoPhaseAuthenticationFactory rapid; 

    public RapidViewService( 
                DefaultViewServiceOptions custom,  
                IViewLoader viewLoader,  
                TwoPhaseAuthenticationFactory rapid) 
        : base(custom, viewLoader) 
    { 
        this.rapid = rapid; 
    } 

    public override Task<Stream> Login(LoginViewModel model, SignInMessage message) 
    { 
        var authenticationRequest = rapid.StartAuthenticationRequest(); 
        authenticationRequest.TwoPhaseAuthenticationKey = message.ReturnUrl; 

        authenticationRequest.Authenticated += (sender, args) => challenge.AnonymousUserId = args.Value; 

        model.Custom = new 
        { 
            Code = authenticationRequest.Challenge, 
            RapidUrls =  new LogonUrls("http://MyIdentityServerAddress/rapid", challenge), 
        }; 

        return base.Login(model, message); 
    } 
}  

Note: This makes use of a TwoPhaseAuthenticationFactory class derived from AuthenticationFactory<T> where T is an AuthenticationRequest - in this case a TwoPhaseAuthenticationRequest:

using Persistence;

public class TwoPhaseAuthenticationFactory : AuthenticationFactory<TwoPhaseAuthenticationRequest>
{
    public TwoPhaseAuthenticationFactory(
                INewAuthenticationRequest<TwoPhaseAuthenticationRequest> store) 
                : base(store)
    {
    }

    public TwoPhaseAuthenticationFactory(
                RapidContext context, 
                INewAuthenticationRequest<TwoPhaseAuthenticationRequest> store) 
                : base(context, store)
    {
    }
}

Step 5. Configure RapID Authentication in your Identity Server

The final step is to modify the Configuration method of the IdentityServer3's OwinStartup class.

You will already have code to register your custom user service, though as that now has a TwoPhaseAuthenticationRequestStore being passed to its constructor, you will need to also register that class in the Startup.Configuration method to enable its construction within your identity server by Autofac.

Additionally though, you need to instantiate the TwoPhaseAuthenticationRequestStore and register it with the RapID library and identity server. Registration with identity server is achieved via the IdentityServerServiceFactory.Register method (along with some other RapID related classes/interfaces) and registration with the RapID library is achieved by setting its FindLogonProvider property. :

Note: The private helper method DefaultViewServiceWithRapidJavascript which is used to inject RapID event handling javascript code into the login page.

public void Configuration(IAppBuilder app) 
{ 
    . 
    . 
    var factory = new IdentityServerServiceFactory() 
    . 
    . 
    var authenticationStore = new TwoPhaseAuthenticationRequestStore(); 

    // Make the RapID library aware of the authentication request store 
    app.UseRapidQr("/rapid", new RapidQrOptions 
    { 
        FindAuthenticationRequestProvider = authenticationStore, 
    }); 

    // Configure IdentityServer to use RapID 
    factory.UserService = new Registration<IUserService, InMemoryRapidUserService>(); 
    factory.ViewService = new DefaultViewServiceRegistration<RapidViewService>(DefaultViewServiceWithRapidJavascript()); 
    factory.Register(new Registration<TwoPhaseAuthenticationFactory>()); 
    factory.Register(new Registration<INewAuthenticationRequest<TwoPhaseAuthenticationRequest>>(authenticationStore)); 
    factory.Register(new Registration<TwoPhaseAuthenticationRequestStore>(authenticationStore)); 
    . 
    . 
    . 
    app.UseIdentityServer(identityServerOptions); 
} 

private DefaultViewServiceOptions DefaultViewServiceWithRapidJavascript() 
{ 
    var options = new DefaultViewServiceOptions(); 
    options.Scripts.Add("/rapid/Content/jquery.sse.js"); 
    options.Scripts.Add("/rapid/Content/eventsource.js"); 
    options.Scripts.Add("/rapid/Content/RapidQrEvents.js"); 
    options.Scripts.Add("/rapid/Content/rapidAutoReload.js"); 
    return options; 
} 

Now, your identity server's login page will include a QR code and when scanned, your end-users will be logged in and ready to go through whatever authorisation process you require.

Nancy

This section describes the process of integrating RapID QR authentication into an OWIN based Nancy website.

The steps outlined below will yield a simple website with RapID QR login. The website will make use of the Rapid.Authentication SDK to display the QR code which your users will be able to scan and be authenticated via the RapID credential that is already stored securely on their mobile device.

Steps 1 to 4 will create a minimal web server, based on the Katana - ASP.NET Host sample in the NancyFx GitHub wiki here. Subsequent steps will add RapID QR authentication to that website.

Step 1. Create an ASP.NET web app

Select an Empty ASP.NET project without folders and core references for Web Forms, MVC or Web API.

Install NuGet packages for Nancy and Owin:

PM> install-package Nancy 
PM> install-package Microsoft.Owin.Host.SystemWeb 
PM> install-package Microsoft.Owin.StaticFiles 
PM> install-package Nancy.Owin 

Step 2. Set up OWIN to use Nancy

Add the following to web.config:

<appSettings> 
    <add key="owin:HandleAllRequests" value="true"/> 
</appSettings> 

And create an OWIN startup class:

using Owin;

public class Startup 
{ 
    public void Configuration(IAppBuilder app) 
    { 
        app.UseNancy(); 
    } 
} 

Step 3. Create a home page Nancy module

Your website will need a page that users can reach without being authenticated. Later, we will add the RapID QR code to this page, but for now we will keep it really simple.

using Nancy; 

public class HomeModule : NancyModule 
{ 
    public HomeModule() 
    { 
        Get["/"] = x => "Hello"; 
    } 
} 

Step 4. Create a secure page

Your website will also have pages that only authenticated users can reach. The Nancy authentication overview has details of how this works. In essence though, you simply create a NancyModule that uses RequiresAuthentication to ensure that the route is not available unless the user has authenticated.

using Nancy; 


public class SecureModule : NancyModule 
{ 
    public SecureModule() : base ("secure") 
    { 
        this.RequiresAuthentication(); 

        Get["/"] = x => $"Welcome {Context.CurrentUser.UserName}!"; 
    } 
} 

Note: If you run your website at this stage and browse to the "/secure" route, you will not see the welcome message because you are not authenticated.

Step 5. Add a reference to the Rapid.Authentication assembly

The Rapid.Authentication SDK that you downloaded includes a NuGet package for the Rapid.Authentication assembly. Extract this locally and install it into your Nancy OWIN solution from the Visual Studio package manager console.

PM> install-package C:\Temp\Rapid.Authentication.n.n.n.nupkg 

Step 6. Set up an authentication request store

The RapID QR code represents a randomly generated authentication (or challenge) code which is encapsulated by the AuthenticationRequest class (namespace Rapid.Authentication). Your Nancy website needs to store an AuthenticationRequest object for each rendered logon page. When an end-user scans the QR code and POSTs to the authentication URL, the RapID library locates the AuthenticationRequest and fires the Authenticated event passing the AnonymousUserId from the end-user's RapID credential.

The Rapid.Authentication SDK includes an in-memory AuthenticationRequest store: InMemoryAuthenticationRequestStore (namespace Rapid.Authentication.Persistence). This will work for single server websites, but you must write your own persistent store for multiple server websites (e.g. server farms, load balanced, etc.). Such persistent AuthenticationRequest object stores must derive from FindAuthenticationRequest<AuthenticationRequest> and must implement the interface INewAuthenticationRequest<AuthenticationRequest> (namespace Rapid.Authenticate.Persistence).

Step 7. Set up Nancy to use RapID QR authentication

Your Nancy website needs to be configured for RapID. This can be done through a custom bootstrapper class, derived from DefaultNancyBootstrapper. Any other customisation of your Nancy website will be placed here too.

using INewBasicLogonRequestStore = Rapid.Authentication.Persistence.INewAuthenticationRequest<Rapid.Authentication.AuthenticationRequest>;

public class CustomerNancyBootstrapper : DefaultNancyBootstrapper
{
    private readonly INewBasicLogonRequestStore rapidAuthenticationStore;

    public CustomerNancyBootstrapper(INewBasicLogonRequestStore authenticationRequestStore)
    {
        rapidAuthenticationStore = authenticationRequestStore;
    }

    protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines) 
    { 
        base.ApplicationStartup(container, pipelines); 
        RapidSessionAuthentication.Enable(pipelines); 
    } 

    protected override void ConfigureApplicationContainer(TinyIoCContainer container) 
    { 
        base.ConfigureApplicationContainer(container); 
        container.Register(rapidAuthenticationStore); 
    } 
} 

With this CustomNancyBootstrapper class in place, you can add code to your OWIN Startup class's Configuration method to hook up the AuthenticationRequest store:

// This must be shared between your website and the RapID library 
var inMemoryAuthenticationRequestStore = InMemoryAuthenticationRequestStore.NewInstance(); 

// Configure the RapID library 
app.UseRapidQr("/rapid", new RapidQrOptions 
{ 
    FindAuthenticationRequestProvider = inMemoryAuthenticationRequestStore, 
}); 

// Configure your website 
app.UseNancy(options => 
{ 
    options.Bootstrapper = new CustomerNancyBootstrapper(inMemoryAuthenticationRequestStore); 
}); 

app.UseStageMarker(PipelineStage.MapHandler);

You are now ready to inject the required RapID authentication objects into your Nancy Module's constructor. You need an AuthenticationFactory<AuthenticationRequest> and a RapidNancyContext:

using Rapid.Authentication; 
using Rapid.Authentication.Website.Nancy; 

public class HomeModule : NancyModule 
{ 
    public HomeModule( 
        AuthenticationFactory<AuthenticationRequest> rapidLogons, 
        RapidNancyContext context) 
    {
        ...
    } 
}

You need to add a RapID QR code to the HomeModule page that you created above. To achieve this using Nancy's Super Simple View Engine you will need a view model class:

using System;
using Nancy;
using Rapid.Authentication;

public class HomeVm
{
    public string Code { get; set; }

    public Uri QrCodeUrl;
    public Url EventStreamUrl;

    public LogonUrls Urls { get; set; }
}

and the HTML to render the QR code will access this view model. Using Master pages and sections this can be achieved as shown here:

Home.html:

@Master['master']

@Section['Title'] RapID QR logon in OWIN based Nancy website. @EndSection

@Section['PageSpacing']col-lg-12@EndSection

@Section['Content']
<div class="col-sm-6 col-md-6 col-lg-6">
    @Partial['logonEvents.html'];
</div>

<div class="col-sm-6 col-md-6 col-lg-6">
    @Partial['logonCode.html']
</div>

@EndSection

@Section['OtherScript']
<script src="~/rapid/Content/eventsource.js"></script>
<script src="~/rapid/Content/jquery.sse.js"></script>
<script src="~/rapid/Content/RapidQrEvents.js"></script>
<script>
    listenForRapidEvents("@Model.Urls.EventsUrl", function () {
            window.location.href = "/secure";
        });
</script>
@EndSection

Note: The script above will listen for RapID events which will cause the redirect to the SecureModule that you set up above when the QR code is scanned and the end-user is authenticated.

master.html:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->

    <title>@Section['Title']</title>
    <!-- Latest compiled and minified CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css" integrity="sha384-fLW2N01lMqjakBkx3l/M9EahuwpSfeNvV63J5ezn3uZzapT0u7EYsXMjQV+0En5r" crossorigin="anonymous">

    <link href="~/Content/rapid.css" rel="stylesheet">
    <link href="~/Content/ie10-viewport-bug-workaround.css" rel="stylesheet">

    <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
    <!--[if lt IE 9]>
        <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
        <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->
    @Section['Head']
</head>
<body>
    <header>
        <img class="banner" src="~/Content/images/heading.jpg" />
    </header>
    <section class="content-wrapper">
        <div class="container">
            <div class="row">
                @Section['Content']
            </div>
            <div class="row">
                @If.ErrorMessage
                <div class="alert alert-danger" role="alert">
                    <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
                    <span class="sr-only">Error:</span>
                    @Model.DisplayErrorMessage
                </div>
                @EndIf
            </div>
        </div>
    </section>

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS" crossorigin="anonymous"></script>
@Section['OtherScript']

</body>
</html>

logonEvents.html:

<div class="form-box">
    <div class="form-content form-content-login">
        <div>
            <h3>Logon Event Stream</h3>
        </div>

        <div class="logon-events-connected">
            <strong>Connection</strong>
            <p style="display:block">Not connected</p>
        </div>

        <div class="logon-events-messages">
            <strong>Events</strong>
        </div>
    </div>
</div>

logonCode.html:

<div class="form-box">
    <div class="form-content form-content-login">
        <div class="qr-code">
            <img id="logon_bitmap" title="Scan QR code to logon." src="@Model.Urls.QrImageUrl" alt="Logon barcode"/>
        </div>
        <div>
            @Model.Code
        </div>
        <p class="qr-description">
            Logon with your phone, or <a href="~/signup">create an account</a>
        </p>
    </div>
</div>

With this html set up, the route handler in the HomeModule can be set up to handle RapID authentication:

public HomeModule( 
    AuthenticationFactory<AuthenticationRequest> rapidLogons, 
        RapidNancyContext context) 
    { 
        Get["/"] = x => HomeRoute(rapidLogons, rapidNancyContext);
    }

    public dynamic HomeRoute(
                AuthenticationFactory<AuthenticationRequest> rapidLogons,
                RapidNancyContext rapidNancyContext)
    {
        var challenge = rapidLogons.StartAuthenticationRequest();
        challenge.Authenticated += (sender, args) => rapidNancyContext.OnAuthenticated(args.Value);

        var viewModel = new HomeVm
        {
            Urls = new LogonUrls(rapidNancyContext.BaseUrl, challenge),
            Code = challenge.Challenge,
        };

        var sessionCookie = new NancyCookie("RapidSessionId", challenge.SessionId, httpOnly: true, secure: false);

        return View["Home", viewModel].WithCookie(sessionCookie);
    }

Add the following to web.config so that the required embedded assets will be loaded by IIS:

<system.webServer>
    <modules runAllManagedModulesForAllRequests="true" />
</system.webServer>

If you run the website now, you should see a RapID QR code and if you scan it (e.g. using the RapID Logon test tool) you will be redirected to the secure page where you will see the anonymous ID from the client certificate that was supplied with the POST to the redirect URL.

QR Code Scanning

QR Code Scanning in iOS

Native iOS QR Code reader

The demo application bundled in the iOS Rapid SDK has a sample QR Code controller (QRCodeScannerViewController) that you can pull in to your own application and make minor modifications to suit your needs.

Summary of implementation

The native iOS QR Code scanner requires AVCaptureSession which is found in the AVFoundation framework. Make your controller an AVCaptureMetadataOutputObjectsDelegate to capture QR Code output and initialise your local AVCaptureSession as follows.

#import <AVFoundation/AVFoundation.h>

@interface QRCodeScannerViewController () <AVCaptureMetadataOutputObjectsDelegate>

//Attach the viewPreview below to some view in the your storyboard.
@property (weak, nonatomic) IBOutlet UIView *viewPreview; 
@property (nonatomic, strong) AVCaptureSession *captureSession;
@property (nonatomic, strong) AVCaptureVideoPreviewLayer *videoPreviewLayer;

@end

@implementation QRCodeScannerViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.captureSession = nil;
}

Create a method to start the QR Code scanner and call this in viewDidAppear

-(BOOL)startReading
{
    NSError *error;

    AVCaptureDevice *captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];

    AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:&error];

    if (!input) {
        NSLog(@"%@", [error localizedDescription]);
        return NO;
    }

    self.captureSession = [[AVCaptureSession alloc] init];
    [self.captureSession addInput:input];

    AVCaptureMetadataOutput *captureMetadataOutput = [[AVCaptureMetadataOutput alloc] init];
    [self.captureSession addOutput:captureMetadataOutput];

    dispatch_queue_t dispatchQueue;
    dispatchQueue = dispatch_queue_create("myQueue", NULL);
    [captureMetadataOutput setMetadataObjectsDelegate:self queue:dispatchQueue];
    [captureMetadataOutput setMetadataObjectTypes:[NSArray arrayWithObject:AVMetadataObjectTypeQRCode]];

    self.videoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.captureSession];
    [self.videoPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
    [self.videoPreviewLayer setFrame:self.viewPreview.layer.bounds];
    [self.viewPreview.layer addSublayer:self.videoPreviewLayer];

    [self.captureSession startRunning];

    return YES;
}


Implement the delegate to capture output

-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
    if (metadataObjects != nil && [metadataObjects count] > 0)
    {
        AVMetadataMachineReadableCodeObject *metadataObj = [metadataObjects objectAtIndex:0];
        if ([[metadataObj type] isEqualToString:AVMetadataObjectTypeQRCode])
        {
            dispatch_async(dispatch_get_main_queue(), ^{
                self.qrCodeContent = [metadataObj stringValue];
                [_captureSession stopRunning];
                _captureSession = nil;
                [_videoPreviewLayer removeFromSuperlayer];
                [self authenticate]; //use the QR code to authenticate
            });
        }
    }
}

QR Code Scanning in Android

Native Android QR Code reader

There is no built in QR Code scanner in Android. The most popular solution is to use the zxing QR Code apk.

Embedding ZXing QR Code scanner inside App

The source can be found on GitHub at the zxing project

You may find the following blog posts useful.

http://ribinsandroidhelper.blogspot.co.uk/2013/03/qr-code-reading-on-your-application.html
http://stackoverflow.com/questions/4854442/embed-zxing-library-without-using-barcode-scanner-app

Quick prototype solution using QR Code App installed on user device

The alternative is to use a QR Code scanner installed on the user's device. This can be a quick mechanism to help you prototype.

Start an intent to show QR Code scanner (e.g. when a button is clicked).

Intent intent = new Intent(
    "com.google.zxing.client.android.SCAN");
intent.putExtra("SCAN_MODE", "QR_CODE_MODE");
startActivityForResult(intent, 0);

Override the onActivityResultMethod to get the QR Code

public void onActivityResult(int requestCode, int resultCode, Intent intent) {

if (requestCode == 0) {
    if (resultCode == RESULT_OK) {

       contents = intent.getStringExtra("SCAN_RESULT"); // This will contain your scan result
       String format = intent.getStringExtra("SCAN_RESULT_FORMAT");
    }
}