Managing Client Credentials

 

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 Link for Device 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

The HP Token Proxy is an implementation of an API proxy provided for free to all Link for Device App developers. Based loosely on RFC7521 and RFC7523, it works with the Link for Device 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:

  1. You allow HP to securely store your app's client secrets in the cloud (App Center)
  2. 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):
    1. Your app requests an app attestation token (a special token that securely proves your app's identity) from Link Services (See Attestation SDK API Details below)
    2. Link Services securely verifies your app's identity with a cloud-based App Attestation service
    3. The App Attestation service issues an app attestation token
    4. Link Services returns the app attestation token to your app
    5. 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)
    6. 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
    7. The HP Token Proxy will forward the resulting request on to the target cloud service's token endpoint
    8. The HP Token Proxy will capture the response from the target cloud service's token endpoint
    9. 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
    10. 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

Link Services and the SDK (version 1.3 and later) includes a new package called com.hp.jetadvantage.link.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 Link Services to create an attestation token, the following conditions must be met:

 

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 on 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 Link for Device 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 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:

  1. Under the "Client credentials" section, click on the "Add" button
  2. 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
  3. Verify the entry now shown in the "Client credentials" section of the "Register new app" form
  4. 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)

Obviously, if you haven't yet passed VAV and published your app you can't use App Center to hold your app's client secrets. So how do you develop and debug your app with the HP Token Proxy before it's published? You use LDB Mode. Here's how:

  1. 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.
  2. Enable LDB Mode on the device (See instructions here)
  3. Using the instructions in the SDK, push your debug credentials into the MFP, making them available to the SDK.
  4. 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 Link for Device Simulator

The same process used with an MFP in LDB mode can be used with the Link for Device 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.