-
Notifications
You must be signed in to change notification settings - Fork 20
2. Single Sign on
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.
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>