Single Sign-on Authentication (SSO) with JWT
FeaturedUpdated article: Admin - Facebook, Twitter & Single Sign-On.
This post has been edited to replace the term OAUTH with JWT. I mistakenly thought we were using OAUTH when we were using a simplified JSON Web Tokens (JWT) option. We are currently working on adding a SAML SSO option as well. -- Bryan
Introduction
Connections Online (COL) provides clients with single sign-on (SSO) capability via JSON Web Tokens (JWT). This allows sites to validate users securely via their own mechanism, including, but not limited to, Microsoft Windows® and Active Directory. When users visit Connections Online, they will automatically be logged in. This document covers how this system works and provides an example for Lightweight Directory Access Protocol (LDAP) lookup.
Once the authentication server is set up, users are given a new COL URL to use to automatically log them in. After a user has logged in once with that URL, if they return to the main COL login page, they will see an extra option to log in with that authentication server (shown in orange and labelled "Sypher.Local" in the screenshot below).
Process
The diagram below shows an overview of the authentication process.
For the system to work, clients must implement an HTTP remote login service that provides the required Access Token to Connections Online. Access tokens must be returned in the form of a JSON Web Token (JWT). Most common languages provide JWT libraries to facilitate this process.
- An unauthenticated user visits Connections Online via a client-specific SSO entry point, https://col.connectionsonline.net/site/[Nickname]. The Nickname is provided to each client. An optional &return_to parameter can be passed to return to a specific page in Connections Online.
- Connections Online recognizes that SSO is enabled for the site and that the user requires remote authentication.
- Connections Online requests authorization access from the remote login URL provided by client.
- The remote login service authenticates user access. The service must return either the Connections Online username or email address for the user.
- The remote login service provides the Access Token to Connections Online. This is done by routing to https://col.connectionsonline.net/auth/internal?site=[Nickname]&jwt=[access-token]. The Access Token payload must consist of:
Attribute |
Description |
iat |
The time the token was generated. The value must be the number of seconds since UNIX epoch. If this number is more than 15 minutes off from Connections Online, the authentication will fail. |
jti |
A unique JSON web token identifier (nonce). Your server should create a new one for every log in attempt. This assures a token is only used once. In our example, we return a GUID for the jti, but any unique value will work. |
User |
Email or Connections Online username of the authorized user. This must uniquely identify the user. If more than one account has the same email address, authentication will fail. |
The token must be an encoded JWT using a Secret Key provided to each client. This Secret Key is then used by Connections Online to decode the message upon receipt.
- Upon receipt of Access Token, Connections Online will decode JWT using the client Secret Key. If the user can be validated, access will be granted. If the user cannot be validated, an error will be displayed and options will be provided to the user to retry or to proceed to the Connections Online 4.1 login screen.
Configuration
Before using single sign-on, a Site Administrator must configure the following settings in Connections Online. These settings can be found on the Single Sign-on tab of the Site Administration page.
Nickname |
A nickname for the site. It is used in the entry point URL (https://col.connectionsonline.net/site/[nickname]) to tell Connections Online the user wishes to do remote authentication. This name must contain only letters and numbers with no spaces. It will be validated on entry. |
Authentication URL |
This is the URL to the client’s remote login service. |
Shared Secret Key |
This value is auto-generated by Connections Online and can be regenerated through site settings. This key should be kept secret to protect security. If compromised, please regenerate immediately. |
Example
The following example is provided for a fictitious Sample Credit Union. The Remote Login Service is developed using an ASP.NET Web Handler file (.ashx). The following key, nickname, and URLs are used for this example:
COL Nickname |
SampleCU |
COL Secret Key |
col_sso_yoursecretkey |
COL SSO Entry Point |
|
Remote Login URL |
|
LDAP Server |
Remote Login Service
The remote login service has to accept an authentication request from a browser and then return an encoded access key back to Connections Online. In our example, this is accomplished with Lightweight Directory Access Protocol (LDAP):
JWTServer.ashx (C#)
using System; using System.Collections.Generic; using System.Configuration; using System.DirectoryServices; using System.Security.Principal; using System.Web; namespace JWTServer { public class JWT : IHttpHandler { public void ProcessRequest(HttpContext context) { //get app settings string secretKey = ConfigurationManager.AppSettings["COLSecretKey"]; string returnServer = ConfigurationManager.AppSettings["COLReturnServer"]; string site = ConfigurationManager.AppSettings["COLSite"]; // return email address from LDAP try { TimeSpan t = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)); double timestamp = t.TotalSeconds; string currentUser = context.Request.LogonUserIdentity.User.Value; // S-ID of user including domain // if no identity, don't validate. if (string.IsNullOrEmpty(currentUser)) { context.Response.Write("User not logged in."); return; } string email = GetEmail(currentUser); var nonce = System.Guid.NewGuid(); var payload = new Dictionary<string, object>() { { "iat", timestamp }, // Must be +/-15 minutes from COL server time { "jti", nonce }, // Keeps this request from being used more than once { "user", email } // User's email address to match up in COL }; string token = JWT.JsonWebToken.Encode(payload, secretKey, JWT.JwtHashAlgorithm.HS256); string redirectUrl = returnServer + "/auth/internal?site=" + site + "&jwt=" + token; // Add return URL string returnTo = context.Request.QueryString["return_to"]; if (returnTo != null) redirectUrl += "&return_to=" + returnTo; // When testing, you can switch between writing and redirecting //context.Response.Write(redirectUrl); context.Response.Redirect(redirectUrl); } catch (Exception ex) { context.Response.Write(ex.Message); } } // Part of the IHttpHandler public bool IsReusable { get { return false; } } private static string GetEmail(string user) { string ret = string.Empty; string path = ConfigurationManager.AppSettings["LDAPConnectionPath"]; // For debugging on machine not in AD //string _path = "WinNT://" + Environment.MachineName; DirectoryEntry entry = new System.DirectoryServices.DirectoryEntry(path); // If your AD requires you to log in to do a search, use this instead: //DirectoryEntry entry = new System.DirectoryServices.DirectoryEntry(path, username, pwd); DirectorySearcher mySearcher = new System.DirectoryServices.DirectorySearcher(entry); // Filter for user with the given SID string filter = string.Format("(&(objectCategory=person)(objectClass=user)(!userAccountControl:1.2.840.113556.1.4.803:=2)(objectSId={0}))", user); mySearcher.Filter = (filter); mySearcher.PropertiesToLoad.Add("mail"); SearchResult result = mySearcher.FindOne(); if (result != null) ret = result.Properties["mail"][0].ToString(); return ret; } // This converts the LDAP ObjectSid field to a string (for debugging) private static string SIDtoString(byte[] sidBinary) { SecurityIdentifier sid = new SecurityIdentifier(sidBinary, 0); return sid.ToString(); } } }
web.config
Configure the site for Windows® Authentication and set application settings. This also registers the .ASHX handler file.
<?xml version="1.0"?> <configuration> <appSettings> <add key="autoFormsAuthentication" value="false" /> <add key="enableSimpleMembership" value="false"/> <!-- The Connections Online server you are using (either https://col.connectionsonline.net or https://beta.connectionsonline.net) --> <add key="COLReturnServer" value="https://col.connectionsonline.net"/> <!-- Your custom values --> <add key="COLSecretKey" value="col_sso_yoursecretkey"/> <add key="COLSite" value="SampleCU"/> <add key="LDAPConnectionPath" value="LDAP://LDAP.samplecu.org/cn=users,DC=sypher,DC=local"/> </appSettings> <system.web> <compilation debug="true" targetFramework="4.5" /> <httpRuntime targetFramework="4.5" /> <authentication mode="Windows"></authentication> <authorization> <deny users="?" /> </authorization> </system.web> <system.webServer> <handlers> <add name="JWT" verb="*" path="/auth/sso" type="JWTServer.JWT" /> </handlers> </system.webServer> </configuration>
The above web site would then be deployed to the Remote Service URL: https://samplecu.org/auth/sso. Connections Online will then request authentication for this URL when a user opens the application from the site entry point: https://col.connectionsonline.net/site/SampleCU
Please sign in to leave a comment.
Comments
0 comments