JSON Web Token SSO - HL Vanilla Community
<main> <article class="userContent"> <p>JSON Web Tokens (JWT) allow the transferring of "claims" from one party to another in a URL-safe format. The JWT consists of two base64_encoded JSON objects with a signature attached. They can be passed as GET parameters or as Bearer tokens. </p><ul><li>For more information about JWT, see <a href="https://tools.ietf.org/html/rfc7519" rel="nofollow noreferrer ugc">JSON Web Token RFC</a>.</li></ul><h2 data-id="jwt-sso-addon-in-vanilla">JWT SSO addon in Vanilla</h2><p>Unlike our other SSO methods (JSConnect, SAML, OAuth2), <strong>JWT SSO </strong>is not intended to be used in conjunction with other Sign On methods. With that said, you <em>can </em>use it with other methods but there will be <strong>no</strong> Sign In button on your Sign In page, as you can see below. </p><ul><li>If you're using JWT SSO as your default connection, your regular Sign In button will go directly to your JWT SSO Sign In URL.</li></ul><div class="embedExternal embedImage display-medium float-none"> <div class="embedExternal-content"> <a class="embedImage-link" href="https://us.v-cdn.net/6030677/uploads/OCE4U6BAU1W0/jwtsso-example.png" rel="nofollow noreferrer noopener ugc" target="_blank"> <img class="embedImage-img" src="https://us.v-cdn.net/6030677/uploads/OCE4U6BAU1W0/jwtsso-example.png" alt="JWTSSO_example.png" height="838" width="1055" loading="lazy" data-display-size="medium" data-float="none"></img></a> </div> </div> <p>As with all our SSO solutions:</p><ul><li>user data passed as part of a JWT payload are mapped to existing Vanilla community accounts by email address, </li><li>or a new account is created if no match is found. You may combine it with any other SSO connections.</li></ul><p>A settings form in the Dashboard allows you to define custom parameter names for:</p><ul><li>Sign In, Register, and Sign Out URLs</li><li>Issuer</li><li>Intended Audience</li><li>Client secret (for testing the JWT signature)</li><li>Expected keys in the JSON response to the profile request (e.g., UniqueID, Email, Username, Photo)</li></ul><h3 data-id="redirecting-back-to-the-vanilla-community">Redirecting back to the Vanilla community</h3><p>When setting up your Sign In URL in the Vanilla Dashboard, you can redirect users to the URL where they initiated the log in process. To do so, add a <strong>target variable </strong>and a <strong>target placeholder string</strong> that will be dynamically replaced when Vanilla redirects the user to your sign in page. For example:</p><div class="embedExternal embedImage display-large float-none"> <div class="embedExternal-content"> <a class="embedImage-link" href="https://us.v-cdn.net/6030677/uploads/307/XE66CPIRSFVO.png" rel="nofollow noreferrer noopener ugc" target="_blank"> <img class="embedImage-img" src="https://us.v-cdn.net/6030677/uploads/307/XE66CPIRSFVO.png" alt="Screen Shot 2020-04-23 at 5.48.45 PM.png" height="216" width="1964" loading="lazy" data-display-size="large" data-float="none"></img></a> </div> </div> <p>As for your sign-in workflow process for the user:</p><ul><li>make sure you maintain that Target value so that when you redirect back to your Vanilla community, it's passed back so that Vanilla can redirect the user back to where they initiated the sign in.</li></ul><p>The entry endpoint for connecting users using JWT SSO is:</p><pre class="code codeBlock" spellcheck="false" tabindex="0">https://[your-forum-domain]/entry/connect/JWTSSO/?authKey=JWTSSODefault&target=[uri-where-signin-initiated] </pre><p>Technically, you can add the JSON Web Token response to this URL, but the best practice would be to put it in the response headers as a Bearer Token:</p><pre class="code codeBlock" spellcheck="false" tabindex="0">https://[your-forum-domain]/entry/connect/JWTSSO/?authKey=JWTSSODefault&jwt=[jwt-response]&target=[uri-where-signin-initiated] </pre><h3 data-id="map-user-profile-values">Map user profile values</h3><p>The JWT SSO workflow sends the user's profile data as a set of key/value pairs as the payload of a JWT object. Vanilla expects those keys to be:</p><pre class="code codeBlock" spellcheck="false" tabindex="0">{ "Email", "Photo", "Name", "FullName", "UniqueID" } </pre><p>If those key names do not correspond to Vanilla's, you must map the key names in the JWT SSO Addon Settings form. For example, if your authentication provider sends the user's profile like this:</p><pre class="code codeBlock" spellcheck="false" tabindex="0">{ "address": "dave@email.com", "pic": "https://cdn.aws.com/ourcdn/davesprofilepic.gif", "name": "Dave", "fullname": "Dave Johnson", "id": 1828838378 } </pre><p>You will want to fill in the form in the Dashboard like this:</p><div class="embedExternal embedImage display-large float-none"> <div class="embedExternal-content"> <a class="embedImage-link" href="https://us.v-cdn.net/6030677/uploads/492/YTVJDHB8ST29.png" rel="nofollow noreferrer noopener ugc" target="_blank"> <img class="embedImage-img" src="https://us.v-cdn.net/6030677/uploads/492/YTVJDHB8ST29.png" alt="Screen Shot 2019-07-29 at 2.42.02 PM.png" height="734" width="1374" loading="lazy" data-display-size="large" data-float="none"></img></a> </div> </div> <h3 data-id="pass-roles-in-the-jwt-payload">Pass Roles in the JWT payload</h3><p>You can pass a user's Role in the profile response. Unlike the values shown above, there is no user interface for mapping the key name for Roles in the Dashboard. </p><ul><li>If you want to pass a user's Role, and have it applied as a Role in the community, it <strong><em>must </em></strong>be passed as "Roles" (case-insensitive, plural "Roles). </li><li>The value can be a comma-separated list of Roles. In order for it to work, it <strong><em>must</em></strong> correspond exactly with name of a Role or Roles configured in your community.</li></ul><p>The example below:</p><pre class="code codeBlock" spellcheck="false" tabindex="0">{ "address": "dave@email.com", "pic": "https://cdn.aws.com/ourcdn/davesprofilepic.gif", "name": "Dave", "fullname": "Dave Johnson", "id": 1828838378, "Roles": "Moderator, Chearleader" } </pre><p>Will assign these two Roles to this user.</p><p>By default, Vanilla will re-assign the user's Roles every time they log in over SSO, giving your authentication provider full control over the user's Roles. </p><p><strong>📝 NOTE</strong>: Cloud customers who want to be able to assign Roles to users in the Vanilla Dashboard <strong>and</strong> have roles <strong>added</strong> over SSO can ask Vanilla Support to configure their community to work in this way. For more details on passing roles over SSO, check out <a href="https://success.vanillaforums.com/kb/articles/179-managing-roles-with-sso" rel="nofollow noreferrer ugc">this article</a>.</p><h3 data-id="pass-the-name-in-the-jwt-payload">Pass the Name in the JWT payload</h3><p>The <code class="code codeInline" spellcheck="false" tabindex="0">Name</code> attribute of the payload becomes the Display Name that is seen when they post and interact in the Vanilla community. </p><ul><li>Passing the name is optional. </li><li>If you want users to choose their own names, leave the name as an empty string or omit it completely and users will be prompted to create a Display Name when they connect for the first time.</li></ul><pre class="code codeBlock" spellcheck="false" tabindex="0">{ "address": "dave@email.com", "pic": "https://cdn.aws.com/ourcdn/davesprofilepic.gif", "name": "", "fullname": "Dave Johnson", "id": 1828838378, "Roles": "Moderator, Chearleader" } </pre><h3 data-id="make-this-connection-your-default-signin-method">Make this connection your default signin method</h3><p>Checking the <strong>Make this connection your default signin method</strong> box in the Settings form will make this the only way to log in to your Vanilla community or create users. This will mean that:</p><ul><li>When you click the default "Sign In" button, it will redirect you to your authentication provider to log in. </li><li>The community's "Username and Password" or "Registration" forms will no longer be accessible to users. </li><li>Users will <strong>not</strong> be able to edit their profiles in the community.</li><li>You <strong>will</strong> be able to redirect users to a configured end-point after they log out.</li></ul><p><strong>If you have created a user natively in the community before setting JWT SSO as the default method for connecting</strong>, but still need to log in using the "Username and Password" form, you can always access it by typing <code class="code codeInline" spellcheck="false" tabindex="0">/entry/password</code> in the address bar of your browser. Also, when you try to connect over SSO and the system detects that you have a user with the same email address in the community, you will be asked to enter the password you set when you created the user. If this becomes problematic, contact your CSM.</p><h3 data-id="logging-out">Logging out</h3><p>If <strong>Make this connection your default signin method</strong> is checked, you can configure a URL where you would like to redirect a user when he/she logs out of your Vanilla community. This URL can be an end-point on your authentication provider that will then log them out of the "parent" system. If you choose to not log them out of the authentication provider and the user navigates back to the community and clicks <strong>Sign In</strong>,<strong> </strong>he/she will be automatically logged in to the community without having to enter a username and password.</p><h2 data-id="troubleshooting-your-jwt-setup">Troubleshooting your JWT setup</h2><p>You may not be sure what data you're sending or how it's formatted. Ask Vanilla Support to turn on <strong>logging</strong>. Then, you can click the <strong>Event Log</strong> link in the left-hand menu to see the logs. Filter on "Event Name" jwt_logging and scroll down to the bottom of the page to view the filtered logs in either JSON or XML format. Search for "profile" to see what key/value pairs are being translated into. There will also be helpful error messages here as well.</p><h2 data-id="set-up-your-jwt-authentication-provider">Set up your JWT authentication provider</h2><p>With most SSO providers, you will have two additional requirements:</p><ul><li>Users must access your community over <code class="code codeInline" spellcheck="false" tabindex="0">https</code></li><li>Your community must contact your Authorization Server using <code class="code codeInline" spellcheck="false" tabindex="0">https</code></li><li>Your Authorization Server will need to allowlist the redirect URI (e.g., <code class="code codeInline" spellcheck="false" tabindex="0">https://[your-forum]/entry/JWTSSODefault</code>) </li></ul><h3 data-id="build-the-jwt-response">Build the JWT response</h3><p>The JWT response comprises two JSON objects:</p><ul><li>The <strong>Header</strong>:</li></ul><pre class="code codeBlock" spellcheck="false" tabindex="0">{ "typ": "JWT", "alg": "HS512" } </pre><ul><li>The <strong>Payload</strong>:</li></ul><pre class="code codeBlock" spellcheck="false" tabindex="0">{ "iss": "https://your-issuing-site.com/", "sub": "12345", "aud": "https://forumdomain.vanillacommunities.com/", "email": "dave@email.com", "displayname": "Dave", "exp": 1564426188, "nbf": 1564425588 } </pre><p> These have been URL safe base64-encoded. This means:</p><ol><li>Any <code class="code codeInline" spellcheck="false" tabindex="0">+</code> symbols have been replaced by <code class="code codeInline" spellcheck="false" tabindex="0">-</code> after being encoded.</li><li>Any <code class="code codeInline" spellcheck="false" tabindex="0">\</code> symbols have been replaced by <code class="code codeInline" spellcheck="false" tabindex="0">_</code> after being encoded.</li><li>Remove any <code class="code codeInline" spellcheck="false" tabindex="0">=</code> symbols stripped from the end.</li></ol><p>They are then signed by: </p><ol><li>Taking the encoded Header and Payload.</li><li>Concatenating them with a <code class="code codeInline" spellcheck="false" tabindex="0">.</code> </li><li>The concatenated string is now hashed using 'hash_hmac' with the algorithm chosen in the Dashboard form (in this example 'HS512' ) and the secret. The secret is presumed to be base64-encoded but if it is<strong><em> NOT</em></strong>, we can manually set a config setting <code class="code codeInline" spellcheck="false" tabindex="0">JWTSSO.DecodeSecret</code> to <code class="code codeInline" spellcheck="false" tabindex="0">FALSE</code>. </li></ol><p>Sample PHP code of how to sign the payload:</p><pre class="code codeBlock" spellcheck="false" tabindex="0">$rawJWTHeader = '{ "typ": "JWT", "alg": "HS512" }'; $rawJWTPayload = '{ "iss": "https://your-issuing-site.com/", "sub": "12345", "aud": "https://forumdomain.vanillacommunities.com/", "email": "dave@email.com", "displayname": "Dave", "exp": 1564426188, "nbf": 1564425588 }'; $secret = '12355'; $alg = 'HS512'; function signJWT($rawJWTHeader, $rawJWTPayload, $secret, $alg) { $header = $this->base64url_encode($rawJWTHeader); $payload = $this->base64url_encode($rawJWTPayload); $jWTString = $header.'.'.$payload; $key = strtr($secret, '-_', '+/'); // the secret is base64_encoded, decode it. $key = base64_decode($key); $rawSignature = hash_hmac($alg, $jWTString, $key, true); $base64encoded = $this->base64url_encode($rawSignature); $signature = trim($base64encoded); $jwt = $header.'.'.$payload.'.'.$signature; return $jwt; } </pre><h3 data-id="test-the-connection">Test the connection</h3><ol><li>Sign in at <code class="code codeInline" spellcheck="false" tabindex="0">https://[auth-domain].com</code> (your actual SSO sign in page).</li><li>Visit <code class="code codeInline" spellcheck="false" tabindex="0">https://[forum-domain].com</code>. Make sure you are not logged in. Clear your cookies, if necessary.</li><li>Visit the “authorize” link described above.</li><li>You should automatically arrive back at <code class="code codeInline" spellcheck="false" tabindex="0">https://[forum-domain].com</code>, but you should now be signed in to Vanilla.</li></ol><h3 data-id="sample-jwt-payload">Sample JWT Payload</h3><p>JWT payload passed back to vanilla:</p><p><a href="unsafe:/entry/connect/JWTSSO/?authKey=JWTSSODefault&jwt=" rel="nofollow noreferrer ugc"><code class="code codeInline" spellcheck="false" tabindex="0">https://[community-url].com/entry/connect/JWTSSO/?authKey=JWTSSODefault&jwt=jwt-signature</code></a></p> </article> </main>