Client Credentials
Most modern cloud APIs issue client credentials (Client Id and Client Secret) to apps (a.k.a. clients) and require the apps to present those credentials on certain calls to the API (a.k.a. OAuth). If a secret is compromised (stolen and misused), the issuer will revoke the secret and issue a new one to the app. If client credentials are embedded in an app's code, that app will cease to function properly when its secret is revoked. And to recover functionality, the app must be upgraded to a version with a new client secret.
App secrets embedded in app code can always be extracted/discovered by a determined hacker, no matter how hard you try to hide them. Therefore it is HP policy that Workpath apps must never embed client secrets. (In fact, if you do so your app will fail VAV!)
Instead, your app must protect its client secrets in one of the following ways:
- Use APIs that do not require client secrets to be provided by your app (many cloud APIs provide alternatives to client secrets)
- Make your API calls through a server-side component (an API proxy) that you control, where you can securely store your secrets
- Use the HP Token Proxy
This page covers the following topics:
- The HP Token Proxy
- Using the HP Token Proxy in Production Mode (after VAV)
- Using the HP Token Proxy in LDB Mode (before VAV)
- Using the HP Token Proxy with the Workpath Simulator
The HP Token Proxy
The HP Token Proxy is an implementation of an API proxy provided for free to all Workpath App developers. Based loosely on RFC7521 and RFC7523, it works with the Workpath SDK (Version 1.3 or later) and firmware (FutureSmart version 4.8 or later) to let your app securely access standard OAuth-based cloud services without embedding any client secrets in your app code. Here's how it works:
- You allow HP to securely store your app's client secrets in the cloud (App Center)
- Whenever your app would normally call a cloud service's token endpoint (the target cloud service) with its client secret (client_credentials grant flow, authorization_code grant flow, refresh_token flow):
- Your app requests an app attestation token (a special token that securely proves your app's identity) from Workpath Services (See Attestation SDK API Details below)
- Workpath Services securely verifies your app's identity with a cloud-based App Attestation service
- The App Attestation service issues an app attestation token
- Workpath Services returns the app attestation token to your app
- Instead of calling the target cloud service's token endpoint directly, your app calls the HP Token Proxy with its client id and attestation token in place of its client secret (See Token Proxy API Details below)
- If the attestation token is valid and the target URL is verified, the HP Token Proxy will substitute your app's client secret for its attestation token from the App Center
- The HP Token Proxy will forward the resulting request on to the target cloud service's token endpoint
- The HP Token Proxy will capture the response from the target cloud service's token endpoint
- The HP Token Proxy will return the target cloud service's response (which will include your bearer token and optional refresh token) back to your app
- Your App uses its bearer and refresh tokens to access the target cloud service's APIs exactly as if it had retrieved them directly from the target cloud service
Attestation SDK API Details
Workpath Services and the SDK (version 1.3 and later) includes a new package called com.hp.workpath.api.attestation.
It provides a method for your app to request an attestation token, as follows:
Result result = new Result();
AppToken token = AttestationService.getAppToken(mContextRef.get(), result);
if(result.getCode() != Result.RESULT_OK) {
Log.e("[APP]", "Error" + result.getCause());
} else {
if (token != null && token.getAppToken() != null) {
String appToken = token.getAppToken();
Log.d("[APP]", "App token : " + appToken + ", expiresIn : " + token.getExpiresIn());
}
}
For Workpath Services to create an attestation token, the following conditions must be met:
- The MFP must be connected to the internet, including setting the web proxy if necessary
- HP Web Services must be enabled on the MFP
(See Advanced Configuration with the HP Embedded Web Server on the Printer Primer page.)
Token Proxy API Details
The HP Token Proxy is hosted at: https://core.api.hp.com.
It provides a single operation to get an access token from a web service using the attestation token in place if your client secret:
Operation: Get Access Token
Name | Get Access Token |
Resource | /tokenProxy/v1/token |
HTTP Method | POST |
Request Payload Format | application/json, application/xml, or application/x-www-form-urlencoded |
Response Payload Format | Same as Request Payload Format |
Input Parameters:
Parameter | Data Type | Required? | Location | Description |
---|---|---|---|---|
Content-Type (standard http header) |
String | Yes | Header |
This content type should be same as the target cloud service's Oauth token request content type. Supported content types are: application/json , application/xml, and application/x-www-form-urlencoded. |
Authorization | String | Conditional | Header |
If the client and secret are not part of the payload, this field is required. If present, the value should be base64(client_id:attestation_token). The HP Token Proxy will look for the client_id and attestation_token in the Authorization header. If present, it will decode and extract the attestation_token. If not present, it will look for the client_id and attestation_token in the payload. |
x-targetServiceURL | String | Yes | Header | This must be set to the target cloud service's own token endpoint. |
x-targetServiceName | String | No | Header | This a reserved field for future use |
x-clientSecretLocation | String | No | Header |
The location where the HP Token Proxy will look for the attestation_token and replace it with client_secret. Only needed if the target cloud service's OAuth payload is not standard. |
Payload | Depends on Content-Type | Yes | Body |
This field will contain the payload for the request to the target cloud service's token endpoint. The HP Token Proxy will look for the client_secret value in the payload to retrieve the attestation_token. The HP Token Proxy will only replace “client_secret” in the payload. |
Payload → client_secret | String | Conditional | Body |
If the client_id and attestation_token are inclded in the Authorization header, this field is not required. This field needs to be populated with the attestation_token, which will be replaced with client_secret by the Hp Token Proxy. |
Payload → client_id | String | Conditional | Body | If the client_id and attestation_token are inclded in the Authorization header, this field is not required. This field value will be used to extract client_secret information from the App Center. |
HTTP Status Codes:
Code | Reason | Description |
---|---|---|
400 | Bad Request | Mandatory parameter missing or payload is wrong format |
401 | Unauthorized | attestation_token is invalid |
500 | Server Error | Internal server errors |
Example HTTP Request 1: client_id and client_secret passed in payload
POST /tokenProxy/v1/token HTTP/1.1
Host: core.api.hp.com
Content-Type: application/x-www-form-urlencoded
Accept: application/json
User-Agent: curl/7.37.0
Content-Length: 180
x-targetServiceURL:https://www.googleapis.com/oauth2/v3/token
grant_type=grant_type&client_id=<my_client_id>&client_secret=<my_attestation_token>&scope=scope&redirect_uri=redirect_uri&refresh_token=refresh_token&code=code
Example HTTP Request 2: client_id and client_secret passed in Authorization header
POST /tokenProxy/v1/token HTTP/1.1
Host: core.api.hp.com
Content-Type: application/x-www-form-urlencoded
Accept: application/json
User-Agent: curl/7.37.0
Content-Length: 180
Authorization:Basic base64(<my_client_id>:<my_attestation_token>)
x-targetServiceURL:https://www.googleapis.com/oauth2/v3/token
grant_type=grant_type&scope=scope&redirect_uri=redirect_uri&refresh_token=refresh_token&code=code
Example HTTP Response:
HTTP/1.1 200 Created
Content-Type: application/json
Cache-Control: no-store
Date: Wed, 19 Dec 2018 07:18:40 GMT
{
"access_token": "ya29.Glx4Bgu_upCuG3-vSyZEeVVM5xp1ZwnVNlBagLCr-0YlBWyaw_...",
"expires_in": 3518,
"scope": "scope",
"token_type": "Bearer"
}
Example Workpath code: authorization_code grant flow for googleapis (using retrofit, an open source library)
@FormUrlEncoded
@POST
Observable> getTokenFromTokenProxy(@Header("Accept") String accept,
@Header("x-targetServiceURL") String tokenUrl,
@Url String url,
@Field("client_id") String client_id,
@Field("client_secret") String app_token,
@Field("grant_type") String grant_type,
@Field("code") String code,
@Field("redirect_uri") String redirect_uri);
ResponseToken() {
String token_type;
String access_token;
String expires_in;
String refresh_token;
}
Subscription subscription = call.getTokenFromTokenProxy(
"application/json",
"https://www.googleapis.com/oauth2/v3/token",
"https://core.api.hp.com/tokenProxy/v1/token",
<my_client_id>,
appToken,
"authorization_code",
"code",
<my_redirect_url>)
.subscribe(response -> {
mResponse = response;
}
Using the HP Token Proxy in Production Mode (after VAV)
When used with an app that has passed VAV (and on a device that is not in LDB mode), the Token Proxy retrieves an app's Client Secrets from the App Center.
When uploading and submitting your app (HPK) to the App Center, you will add your app's client credentials on the "Register new app" form as follows:
- Under the "Client credentials" section, click on the "Add" button
- Enter a friendly name for the web service (one you can remember easily), the Client Id, the Token Url, the Client Secret, and press the "Add" button
- Verify the entry now shown in the "Client credentials" section of the "Register new app" form
- Repeat as needed for any other cloud services your app uses
Note: If your client credentials change in the future, even after your app has been published, you can always come back to this form and edit them.
Using the HP Token Proxy in LDB Mode (before VAV)
If your app hasn't yet passed VAV, the Token Proxy won't be able to retrieve any of your app's Client Secrets from the App Center. So how do you develop and debug your app with the HP Token Proxy before it has passed VAV? You use LDB Mode. Here's how:
- Acquire a separate set of client credentials for debug purposes only. These "debug credentials" will not be protected, so you need to be able to throw them away if they're stolen/revoked.
- Enable LDB Mode on the device (See instructions here.)
- Establish an ADB connection to the MFP (adb connect <device_ip_address>:5555)
- Use the HPKTool to push your debug credentials into the MFP, making them available to the SDK
- From the Actions ment, select Application Attestation
- Select the HPK file (the app's UUID will be filled in automatically)
- Enter the printer's Hostname/IPAddress and Administrator password (if set)
- Enter your developers.hp.com account name (Username) and LDB Service Key. These are used by the Attestation Service to prove you are a registered developer. (Don't have an LDB Service Key? Create one here.)
- Edit the json to include the debug ClientId and ClientSecret used by your app to access the target cloud service's token endpoint (enter one pair for each target service)
- The HPKTool requires adb to push the debug credentials to the MFP. If adb is not in the path, you may browse to its location by pressing "Select Folder"
- Press "Update" to push the debug credentials to the MFP
- Test your app as normal, no code changes are needed.
In LDB Mode, the SDK will actually embed your app's debug client credentials in the attestation_token (a.k.a. a debug_attestation_token). In this case, the HP Token Proxy will use the credentials in the debug_attestation_token instead of credentials stored in App Center.
Using the HP Token Proxy with the Workpath Simulator
The same process used with an MFP in LDB mode can be used with the Workpath Simulator. See the SDK for details.
Note: The Simulator only supports debug credentials for attestation. In other words, if debug credentials have not been provided to the Simulator when the app requests an attestation token, an attestation token will not be returned.