mRDC Authentication Guide
Introduction
Web service authentication for JHA mRDC™ is handled with a security token that must be included in the header of all authenticated requests. The security token is first obtained by sending a token request to a separate authentication endpoint. The security token request includes user credentials that allow the EPS system to validate and authenticate a user.
If the token request is successful, a security token will be returned. This security token must be included each subsequent request to the JHA SmartPay Mobile Remote Deposit Complete™ (mRDC) web service by sending it as a bearer token in the header of each request. The security token expires after 15 minutes. After the token expires, you will need to obtain a new security token to continue using the web service.
Getting Started
Task covered in this guide
Authenticate a customer into the mRDC web service workflow
Required prerequisites before you continue
- You will need to obtain a shared secret if you are going to be using single sign-on (SSO) authentication. This should have been part of your demo credential set.
- If you are going to be using SSO authentication, you will need an active JHA SmartPay Business™ user and their UserNumber to connect.
- If you are going to use store credentials authentication, make sure you have your JSON Web Key, Entity ID, Store ID, and Client ID (these should have already been provided to you).
Demo Merchant
You will receive a set of demo credentials and a demo merchant at the beginning of your integration. Instead of a sandbox environment (endpoint), you will be coding/testing against this demo merchant in the live system. These demo credentials will only be valid for your non-processing demo merchant, which means that the transactions will process on our system but will not be sent out through the check clearing networks. At the completion of the API certification process, you will be provided your production credentials. For more information about the demo merchant, please speak with your Implementation Coordinator or the EPS Tech Integrations team.
When and How Often to Authenticate
Authentication should be used sparingly and should occur when a user first initiates deposit activities in the front-end application, meaning they either intend to make a deposit or view their deposit history. Once authenticated, since a new security token is returned with each request/response thereafter, you should not authenticate the user again unless the session has 15 minutes of no activity. After 15 minutes, the most recent security token will have expired and a new authenticate request will be necessary.
Authentication Methods
SSO Authentication Method
Integrators who have an already authenticated user in their platform and want to connect to a matching user on the EPS side—leveraging all of the stored EPS permissions and entitlements for that user—will want to use the SSO method of authentication. Using this method, you will send an HTML form using an HTTP POST request to the authentication endpoint (https://smartpay.profitstars.com/auth/connect/token). The request should be sent using the credentials below (the shared secret is only for generating the hash value and should not be sent in the request). Once the token has been generated, you will send it as a bearer token in the header of each subsequent request to the mRDC web service.
All values described below are required.
| NAME | DESCRIPTION |
|---|---|
| client_id | This identifies the authentication type. For SSO authentication, you can use the value MobileRDCSSO. |
| grant_type | This defines the type of credentials being granted. The value should be client_credentials. |
| scope | This is the scope of the request. The value should be: apiaccess. |
| user_number | A unique string value representing the individual that exists in the EPS platform to authenticate. This value is controlled by the FI or the integrator and typically relates back to an online banking user value of some kind. |
| fi_identifier | A unique credential value provided by EPS that identifies the specific merchant entity for the FI. It is important to note that this value represents only a single mRDC entity for the FI. An FI may have multiple mRDC entities; therefore, this value will be unique and new for each new mRDC entity that is boarded. It is not a true FI or owner-level value. |
| timestamp | Current Date and time of the SSO authentication. This must be within 10 minutes of the EPS server time. Required to be in the following format: m/d/yyyy h:mm:ss tt. All timestamps must be converted to Central Time. |
| salt | Shared salt value to make the hash unique. This value is generated and supplied by you as the integrator. |
| hash | A unique string value. Hashed value (either SHA256 or SHA512) used for comparison. |
| type | This identifies the hash calculation type. Valid values are: SHA256 and SHA512. |
| phone_key | This identifies the mobile device and should be unique to each mobile device accessing the system. This value is generated by you as the integrator. |
| shared secret | A unique string value generated and provided by EPS to be stored securely on the integrator's system for hashing. NOT for passing in the request. |
Example hash and resulting string
The string values must be concatenated in this order: user_number + timestamp + fi_identifier + SharedSecret + salt.
Example values: 1234 + 6/17/2019 7:20:40 PM + 5678 + abcd1234 + xyz
Resulting string to hash using: 12346/17/2019 7:20:40 PM5678abcd1234xyz
Resulting hash using the SHA256 method: a189729c2292d323131a5c14cf351f3fa8507928d3f8904f9c9eee9b2c5e3b291
NOTE 1: The hash must be in all lowercase as shown above.
NOTE 2: The timestamp used for the hash calculation should be in the format m/d/yyyy h:mm:ss tt. (e.g. 6/17/2019 7:20:40 PM).
This timestamp format is used in both the token request and the hash calculation for both XML and JSON requests and must also be converted to Central Time.
Resulting hash using the SHA256 method: a189729c2292d323131a5c14cf351f3fa8507928d3f8904f9c9eee9b2c5e3b291
The hash must be in all lowercase as shown above.
Example SSO Token request
Example SSO SHA256 Token Request:
client_id: "MobileRDCSSO"
grant_type: "client_credentials"
scope: "apiaccess"
user_number: "1234"
fi_identifier: "5678"
timestamp: "6/17/2019 7:20:40 PM"
salt: "xyz"
hash: "a189729c2292d323131a5c14cf351f3fa8507928d3f8904f9c9eee9b2c5e3b291"
type: "SHA256"
phone_key: "123test"
Example SSO Token JSON Response
{
"access_token": "xxxxxxxxxxxxxxx",
"expires_in": 900,
"token_type": "Bearer",
"scope": "apiaccess"
}
Store Credentials Method
Integrators who want to have full control over user entitlements and access that comes from their system will want to use the Store Credentials method of authentication. This allows all deposit activity to be submitted under one “user” set of credentials on the EPS side, which means that there will be no need to keep up with multiple user logins in EPS. Since all activity using this method takes place under one user, deposit history using the GetBatches and GetItems methods will be grouped together. Typically, integrators using this method employ their own method for filtering deposit history to their users so that they see the right data. This method will require EPS to generate a JSON Web Key (JWK) for use in generating the client assertion to request the security token. Once the token has been generated, you will send it as a bearer token in the header of each subsequent request to the mRDC web service.
The store credentials consist of the properties shown below.
All values described below are required.
| NAME | DESCRIPTION |
|---|---|
| Entity ID | The Entity ID is a unique identifier that is systematically generated when the merchant is installed on the EPS system. It is also referred to as the Merchant ID or MID. |
| Client ID | A unique user ID systematically generated by EPS upon creating a new JWK for an organization. The Client ID can be unique to each individual merchant, or a main Client ID can be assigned at the reseller/owner level. |
| Store ID | This is the unique profile that the key is tied to for permissions purposes. |
| JSON Web Key | This is a private key that should be stored encrypted on your end. This key will be supplied to you by the EPS Tech Integrations team, if you do not already have one. The key is used in generating your client assertion. |
Generating the Client Assertion
A client assertion should be generated using the following values: client_id, issuer_url, and jwk_text. This resulting client assertion is then used to request the access token for authentication to the API.
See below for an example of generating a client assertion using JavaScript. For examples of generating a client assertion using .NET, click this link.
Once the client assertion has been generated, proceed to request the access token by following the next steps below.
Example Client Assertion Creation:
// Declare initial variables
const issuer_url = "https://smartpay.profitstars.com/auth";
const client_id = "your client id here";
const jwk_text = "your private key here";
// Check to make sure that the jwk json text is set
if (!jwk_text) {
throw new Error("Missing jwk json value");
}
let parsed = JSON.parse(jwk_text);
// If a JWKS wrapper is provided, use the first key
if (parsed.keys && Array.isArray(parsed.keys)) {
if (!parsed.keys.length) {
throw new Error("JWKS contains no keys");
}
parsed = parsed.keys[0];
}
// Parse the jwk text to a json object
const jwk = parsed;
// Throw an error if jwk text does not parse to a json object
if (!jwk || typeof jwk !== "object") {
throw new Error("Parsed JWK is not an object");
}
// Ensure that the jwk object has a sign operation
jwk.key_ops = ["sign"];
// Helper function for encoding to base64
function base64UrlEncode(buf) {
return Buffer.from(buf)
.toString("base64")
.replace(/=/g, "")
.replace(/\+/g, "-")
.replace(/\//g, "_");
}
// Check key type and set correct config for that key type
function getSigningConfig(jwk) {
if (!jwk.kty) {
throw new Error("JWK is missing kty");
}
if (jwk.kty === "RSA") {
return {
jwtAlg: "RS256",
importAlgorithm: {
name: "RSASSA-PKCS1-v1_5",
hash: { name: "SHA-256" }
},
signAlgorithm: {
name: "RSASSA-PKCS1-v1_5"
}
};
}
if (jwk.kty === "EC") {
if (!jwk.crv) {
throw new Error("EC JWK is missing crv");
}
if (jwk.crv === "P-256") {
return {
jwtAlg: "ES256",
importAlgorithm: {
name: "ECDSA",
namedCurve: "P-256"
},
signAlgorithm: {
name: "ECDSA",
hash: { name: "SHA-256" }
}
};
}
if (jwk.crv === "P-384") {
return {
jwtAlg: "ES384",
importAlgorithm: {
name: "ECDSA",
namedCurve: "P-384"
},
signAlgorithm: {
name: "ECDSA",
hash: { name: "SHA-384" }
}
};
}
if (jwk.crv === "P-521") {
return {
jwtAlg: "ES512",
importAlgorithm: {
name: "ECDSA",
namedCurve: "P-521"
},
signAlgorithm: {
name: "ECDSA",
hash: { name: "SHA-512" }
}
};
}
throw new Error(`Unsupported EC curve: ${jwk.crv}`);
}
throw new Error(`Unsupported JWK kty: ${jwk.kty}`);
}
// Import key to crypto key for signing
async function importJwkToCryptoKey(jwk, config) {
return await crypto.subtle.importKey(
"jwk",
jwk,
config.importAlgorithm,
false,
["sign"]
);
}
// Function for signing key
async function signJwt(header, payload, jwk, config) {
const encoder = new TextEncoder();
const headerB64 = base64UrlEncode(encoder.encode(JSON.stringify(header)));
const payloadB64 = base64UrlEncode(encoder.encode(JSON.stringify(payload)));
const unsignedToken = `${headerB64}.${payloadB64}`;
const key = await importJwkToCryptoKey(jwk, config);
const signature = await crypto.subtle.sign(
config.signAlgorithm,
key,
encoder.encode(unsignedToken)
);
const signatureB64 = base64UrlEncode(signature);
return `${unsignedToken}.${signatureB64}`;
}
// Set key config
const config = getSigningConfig(jwk);
// Set claims
const now = Math.floor(Date.now() / 1000);
const exp = now + 60;
const claims = {
sub: client_id,
jti: pm.variables.replaceIn("{{$guid}}"),
iat: now,
nbf: now,
exp: exp,
iss: client_id,
aud: issuer_url
};
// Set header
const header = {
alg: config.jwtAlg,
typ: "client-authentication+jwt"
};
// If key has a kid, assign key kid to header kid
if (jwk.kid) {
header.kid = jwk.kid;
}
//Sign the jwt
const jwt = await signJwt(header, claims, jwk, config);
Requesting Access Token
Once you have generated your client assertion, you can request an access token by submitting an HTML form via an HTTP POST command to the token request URL: https://smartpay.profitstars.com/auth/connect/token. See the table below for a list of the key value pairs that need to be submitted via HTML form to the authentication endpoint.
| KEY | VALUE |
|---|---|
| entity_id | Put your Entity ID here. |
| client_id | Put your Client ID here. |
| store_id | Put your Store ID here. |
| client_assertion | Put your generated client assertion here. |
| grant_type | client_credentials. |
| client_assertion_type | urn:ietf:params:oauth:client-assertion-type:jwt-bearer |
| scope | apiaccess |
Example Store Credentials Token Request
grant_type:client_credentials
scope:apiaccess
client_assertion_type:urn:ietf:params:oauth:client-assertion-type:jwt-bearer
client_assertion:your client_assertion here
client_id:your client_id here
entity_id:your entity_id here
store_id:your store_id here
Example Store Credentials Token JSON Response:
{
"access_token": "xxxxxxxxxxxxxxx",
"expires_in": 900,
"token_type": "Bearer",
"scope": "apiaccess"
}
User Credentials and MFA Credentials Methods
Third-party applications will generally not want to use these methods for authentication. Please speak with the EPS Tech Integrations team if you are considering using these methods.
Using Access Token
Once you have been provided an access token by the RequestAccessToken response, you will set the Authorization header with your access token as a bearer token. You will do this for each subsequent web service request to authenticate each request. The token lasts 15 minutes before expiring. After it expires, you will need to send a new request to the token endpoint for a new authentication token. See below for an example web service request.
Example Web Service Request:
- "Method": POST
- "Content-Type": application/JSON
- "Authorization": Set the Bearer Token in the authorization.
{
"__type":"GetSettingsRequest:#JackHenry.Eps.Mobile.RDC",
"RequestDate":"/Date(1692650646000-0500)/",
"RequestId":"f89ur93iu983uj98ru923",
"Credentials":{
"PhoneKey":"12345testdevice",
"SecurityToken":null
}
}
Handling Errors and Failed Requests
Not all requests will be successful. Below are a few common error messages you should be ready to handle.
- mRDC AuthenticateSSO Failure - This can appear for multiple reasons, the most common of which are a mismatched hash in the request, a timestamp that is out of sync, or the user is not enrolled. For any scenario other than the user not being enrolled, please reach out through your normal support channels for assistance.
{
"error": "Authentication failed"
}
- mRDC AuthenticateSSO Invalid Hash Length Failure – This error response will be returned if the hash being sent in the request is an invalid length. Check to make sure you are using the proper hashing algorithm that is compatible for the request type. Either SHA 256 or SHA 512.
{
"error": "Hash Length is Invalid"
}
- Invalid Client Error - This can happen for a couple of reasons: Either your client_id or entity_id are incorrect. To resolve this error, confirm that you provided the correct entity_id and/or client_id.
{
"error": "invalid_client"
}
- Unauthorized Client Error - This means that the client that you are trying to request a token for is not authorized for the token request endpoint. First, confirm that you are using the correct entity_id and client_id. If the entity_id and client_id are correct, you probably don't have authorization for the entity that you are trying to connect to. Contact EPS Support to request that the entity you are trying to connect to is authorized for the authentication service.
{
"error": "unauthorized_client"
}
- Invalid Scope Error - This means that your scope is invalid. The scope should be "apiaccess".
{
"error": "invalid_scope"
}
- Invalid Request Error - This means that your HTTP request type is not valid. The token request endpoint only accepts HTTP POST requests.
{
"error": "invalid_request"
}
- Unsupported Grant Type Error - This means that your grant_type is not valid. The grant_type should be "client_credentials".
{
"error": "unsupported_grant_type"
}
Next steps
-
Review the API Reference - This guide is a starting point to show how to authenticate an mRDC customer. Please review the API Reference to see all APIs and their technical specifications.
-
Explore other guides - We have other guides to show how to leverage our APIs in other common use cases. If your situation or question is not covered in the current guide, consult another resource.
-
Get certified and move into production - Ready to put your new code into production use? Refer to this process guide that explains our certification steps and how to contact us to get started.