Skip to content
This repository has been archived by the owner on Dec 20, 2024. It is now read-only.

2. Single Sign on

Michael Johnson edited this page Mar 2, 2016 · 8 revisions

SnapMD Single Sign-on (SSO)

SnapMD provides JWT (JSON Web Token) authentication via asymmetrical authentication. Third-party developers will generate their own public/private key pair, and provided the public key to SnapMD developers, who will activate the account for SSO.

The architecture of our JWT option is similar to that used by Zendesk

You start with a token that looks like this:

{
    "typ": "JWT",
    "alg": "http://www.w3.org/2001/04/xmldsig-more#hmac-sha512"
}.{
    "unique_name": "Aaron",
    "email": "[email protected]",
    "role": "patient",
    "iss": "flashmd",
    "aud": "snapmd",
    "exp": "1445382649"
};

This payload is then encrypted using the private key, and appended to a querystring

https://sandbox.connectedcare.md/customer.access?jwt=token_goes_here

Once you have the querystring, nothing fancy is required, such as a web service call or anything. The customer access handler can be accessed via a regular HTTP GET request through a browser.

JwtCompiler example

The example below is an actual snippet from our own internal SSO MVC page and uses the token libraries included in the SnapMD.VirtualCare.LeopardonSso library.

using System;
using System.Security.Cryptography;
using System.Threading.Tasks;
using System.Web.Mvc;
using SnapMD.VirtualCare.LeopardonSso;

namespace SnapMD.IdentityServer.Controllers
{
    public class LoginController : Controller
    {
        private readonly ISnapAuthenticationManager _userManager;

        /// <summary>
        /// Provides an authentication token to the requestor based on a valid login.
        /// //http://forums.asp.net/t/1969451.aspx?Extending+webapi+bearer+token+to+contain+custom+data
        /// </summary>
        /// <param name="model"></param>
        /// <returns>A JSON object containing an access_token field.</returns>
        /// 
        [HttpPost]
        public async Task<ActionResult> Login(LoginRequest model)
        {
            if (!ModelState.IsValid || !model.IsValid)
            {
                ModelState.AddModelError("error", "Missing username and pw");
                return View("index");
            }

            // Authenticate our own user internally
            var user = await _userManager.FindAsync(model.Email.Trim(), model.Password.Trim(), 1, 126);

            if (user == null)
            {
                ModelState.AddModelError("", "Invalid username or password.");
                return View("Index");
            }

            if (user.IsDisabled())
            {
                ModelState.AddModelError("", "User is disabled.");
                return View("Index");
            }

            string redirect;

            // Create an RSA instance with our private key
            using (var rsa = GetRsa())
            {
                // Use the JwtCompiler class (included in this library) to generate the token.
                using (var jwt = new JwtCompiler(rsa))
                {
                    redirect = await jwt.GetRedirectPath(model.Email, model.Email);
                }
            }
            
            // Redirect the user to the VirtualCare application.
            return Redirect(redirect);
        }

        private static RSA GetRsa()
        {
            string path = AppDomain.CurrentDomain.BaseDirectory + @"certs\ppk.xml";

            if (!System.IO.File.Exists(path))
            {
                throw new InvalidOperationException("Missing private keys.");
            }

            var rsa = new RSACryptoServiceProvider(2048);
            rsa.FromXmlString(System.IO.File.ReadAllText(path));
            return rsa;
        }
    }
}

Your Web.config would be modified to include the sections below.

  <configSections>
    <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <section name="SnapMD.VirtualCare.LeopardonSso.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
    </sectionGroup>
  </configSections>

And:

  <applicationSettings>
    <SnapMD.VirtualCare.LeopardonSso.Properties.Settings>
      <setting name="JwtSignOnUrl" serializeAs="String">
        <value>http://emerald.snap.local/customer.access?jwt={0}</value>
      </setting>
      <setting name="HospitalId" serializeAs="String">
        <value>126</value>
      </setting>
    </SnapMD.VirtualCare.LeopardonSso.Properties.Settings>
  </applicationSettings>
Clone this wiki locally