We provide Single Sign-On (SSO) capability to Partner/Enterprise Customers who wish to allow their users (e.g. members, clients, customers, franchisees, staff, etc.) to seamlessly and securely log into our dashboard without being prompted for a username and password. For security, SSO is currently available only on client-level user accounts, not admin or staff accounts.
Warning:
Grade.us does not provide SSO implementation support. Partner/Enterprise users must have 1) necessary development resources to implement SSO, and 2) white-labeled their dashboard.Prior Reading
In This Article:
SSO Access
Once SSO is enabled for your account, your primary admin and staff users will have the power to grant SSO access to any and all of your sub-users at the client-user account level. SSO feature does not apply to admin or staff account level users. Here is the basic workflow for utilizing the SSO feature:
1. Partner/Enterprise tier customers can request this feature by contacting support@grade.us.
2. Grade.us turns the SSO feature on and provides the customer with the SSO Secret.
3. Customer develops the necessary code to generate JWT tokens to integrate their site with ours.
4. Customer appends the JWT token(s) to our authorization URL: https://www.grade.us/sso/authorize.
SSO Secret
For SSO credentials, we use an open standard (RFC 7519) called JSON Web Token (JWT). JWT is a compact and self-contained way for securely transmitting information between parties as a JSON object.
JWTs can be verified and trusted because they are digitally signed. In our implementation, JWTs must be signed with the HMAC algorithm using a secret. You will be issued this secret for your accounts when SSO is enabled.
If you need to debug your JWT token, you can go to https://jwt.io to see the information you are sending to our endpoint. Make sure to use the correct headers in the correct order:
{ "typ": "JWT", "alg": "HS256" }
SSO Token (JWT Tokens)
The information you will need to send in the JWT token is the email address of client-level user, and the current date:
- Email: The email address of the user you want to log in. Make sure that the email address in the JWT is identification to the client-users email address within Grade.us.
- Date: The date on which you are submitting your request. Send the current date to avoid authorization errors.
This information must be transmitted as a JWT token. When creating your JWT token, please use your SSO Secret, and the HMAC algorithm. Then, you can request your login to https://www.grade.us/sso/authorize/.
While this approach makes use of standard algorithms that are part of any cryptography library, there are numerous free and open-source JWT libraries available that make generating the necessary token a snap.
Caution:
Make sure that the authorization URL uses your white-labeled domain or subdomain instead of https://www.grade.us (e.g. https://www.myreviews.com/sso/authorize/).
Example Code
A simple PHP example class for JWT:
<?php /** * JSON Web Token implementation, based on this spec: * http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-06 * * PHP version 5 * * @category Authentication * @package Authentication_JWT * @author Neuman Vong <neuman@twilio.com> * @author Anant Narayanan <anant@php.net> * @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD * @link https://github.com/firebase/php-jwt */ class JWT { /** * Decodes a JWT string into a PHP object. * * @param string $jwt The JWT * @param string|null $key The secret key * @param bool $verify Don't skip verification process * * @return object The JWT's payload as a PHP object * @throws UnexpectedValueException Provided JWT was invalid * @throws DomainException Algorithm was not provided * * @uses jsonDecode * @uses urlsafeB64Decode */ public static function decode($jwt, $key = null, $verify = true) { $tks = explode('.', $jwt); if (count($tks) != 3) { throw new UnexpectedValueException('Wrong number of segments'); } list($headb64, $bodyb64, $cryptob64) = $tks; if (null === ($header = JWT::jsonDecode(JWT::urlsafeB64Decode($headb64)))) { throw new UnexpectedValueException('Invalid segment encoding'); } if (null === $payload = JWT::jsonDecode(JWT::urlsafeB64Decode($bodyb64))) { throw new UnexpectedValueException('Invalid segment encoding'); } $sig = JWT::urlsafeB64Decode($cryptob64); if ($verify) { if (empty($header->alg)) { throw new DomainException('Empty algorithm'); } if ($sig != JWT::sign("$headb64.$bodyb64", $key, $header->alg)) { throw new UnexpectedValueException('Signature verification failed'); } } return $payload; } /** * Converts and signs a PHP object or array into a JWT string. * * @param object|array $payload PHP object or array * @param string $key The secret key * @param string $algo The signing algorithm. Supported * algorithms are 'HS256', 'HS384' and 'HS512' * * @return string A signed JWT * @uses jsonEncode * @uses urlsafeB64Encode */ public static function encode($payload, $key, $algo = 'HS256') { $header = array('typ' => 'JWT', 'alg' => $algo); $segments = array(); $segments[] = JWT::urlsafeB64Encode(JWT::jsonEncode($header)); $segments[] = JWT::urlsafeB64Encode(JWT::jsonEncode($payload)); $signing_input = implode('.', $segments); $signature = JWT::sign($signing_input, $key, $algo); $segments[] = JWT::urlsafeB64Encode($signature); return implode('.', $segments); } /** * Sign a string with a given key and algorithm. * * @param string $msg The message to sign * @param string $key The secret key * @param string $method The signing algorithm. Supported * algorithms are 'HS256', 'HS384' and 'HS512' * * @return string An encrypted message * @throws DomainException Unsupported algorithm was specified */ public static function sign($msg, $key, $method = 'HS256') { $methods = array( 'HS256' => 'sha256', 'HS384' => 'sha384', 'HS512' => 'sha512', ); if (empty($methods[$method])) { throw new DomainException('Algorithm not supported'); } return hash_hmac($methods[$method], $msg, $key, true); } /** * Decode a JSON string into a PHP object. * * @param string $input JSON string * * @return object Object representation of JSON string * @throws DomainException Provided string was invalid JSON */ public static function jsonDecode($input) { $obj = json_decode($input); if (function_exists('json_last_error') && $errno = json_last_error()) { JWT::_handleJsonError($errno); } else if ($obj === null && $input !== 'null') { throw new DomainException('Null result with non-null input'); } return $obj; } /** * Encode a PHP object into a JSON string. * * @param object|array $input A PHP object or array * * @return string JSON representation of the PHP object or array * @throws DomainException Provided object could not be encoded to valid JSON */ public static function jsonEncode($input) { $json = json_encode($input); if (function_exists('json_last_error') && $errno = json_last_error()) { JWT::_handleJsonError($errno); } else if ($json === 'null' && $input !== null) { throw new DomainException('Null result with non-null input'); } return $json; } /** * Decode a string with URL-safe Base64. * * @param string $input A Base64 encoded string * * @return string A decoded string */ public static function urlsafeB64Decode($input) { $remainder = strlen($input) % 4; if ($remainder) { $padlen = 4 - $remainder; $input .= str_repeat('=', $padlen); } return base64_decode(strtr($input, '-_', '+/')); } /** * Encode a string with URL-safe Base64. * * @param string $input The string you want encoded * * @return string The base64 encode of what you passed in */ public static function urlsafeB64Encode($input) { return str_replace('=', '', strtr(base64_encode($input), '+/', '-_')); } /** * Helper method to create a JSON error. * * @param int $errno An error number from json_last_error() * * @return void */ private static function _handleJsonError($errno) { $messages = array( JSON_ERROR_DEPTH => 'Maximum stack depth exceeded', JSON_ERROR_CTRL_CHAR => 'Unexpected control character found', JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON' ); throw new DomainException( isset($messages[$errno]) ? $messages[$errno] : 'Unknown JSON error: ' . $errno ); } } ?>
And an example showing how to use it:
<?php const AUTH_BASE_URL = 'https://www.grade.us/sso/authorize/'; $secret = 'your secret'; date_default_timezone_set('UTC'); $data = array( 'email' => 'master-demo@reviewsoftware.org', 'date' => date("Y-m-d H:i:s") ); $token = JWT::encode($data, $secret, 'HS256'); echo AUTH_BASE_URL . $token; //
You can also download some functional examples here.
End of Article:
You can now provide Single Sign-On capability to your client-level users.
Comments
0 comments
Article is closed for comments.