Security

Security and Authentication

Token Signing Process

This document assumes there is an enrolled and active user created on the host app's DriveSync� server using the Enrolment API or through Business Center. Interacting with the IMS Enrolment API and Business Center is out of the scope of this document. If you have questions about the product please contact us.

Mobile App Signing Interface

IMS SDK validates the identity of actively enrolled users via JSON Web Tokens. The host app is responsible for providing the SDK with a TokenSigner object that will make a request to the host's back end. The successful network response will provide a signed JWT, which is then provided back to the SDK.

The SDK will use the TokenSigner by providing it with an unsigned JWT prototype; e.g. a JWT with a header and payload only. The TokenSigner will provide a signed JWT to the SDK via a callback. Upon reception of the signed JWT, the SDK will verify that all the required fields are provided in the JWT (see in code documentation for more details) before using it to make network calls to the IMS back end.

NOTE: For more information on JWTs, go here.

Example: Unsigned JWT Prototype

// header
{
    "alg": "RS256",
    "typ": "JWT"
}

// payload
{
    "iss": "63d9a389-4ddc-4df1-8dd1-af6a10c7226e",
    "sub": "14",
    "jti": "BD988B2E-B16A-49B8-8882-9B3C9D35DD84",
    "nbf": 1497577381,
    "exp": 1497578281
}

// Encoded Unsigned JWT provided to `TokenSigner` by the SDK
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI2M2Q5YTM4OS00ZGRjLTRkZjEtOGRkMS1hZjZhMTBjNzIyNmUiLCJzdWIiOiIxNCIsImp0aSI6IkJEOTg4QjJFLUIxNkEtNDlCOC04ODgyLTlCM0M5RDM1REQ4NCIsIm5iZiI6MTQ5NzU3NzM4MSwiZXhwIjoxNDk3NTc4MjgxfQ

NOTE: The window of time a JWT is valid can be no longer than 15 minutes. i.e. exp - nbf <= 15 minutes

Token Signer

As stated previously, the host app must provide the SDK with an object that conforms to the TokenSigner protocol/interface to sign tokens.

Java

public interface TokenSigner {
    public void sign(@NotNull String unsignedToken, @NotNull ResultCallback<String> callback);
}

Swift

public protocol TokenSigner {
    func sign(_ unsignedToken: String, resultHandler: @escaping ResultHandler<String>)
}

Example Implementations

Java

class MyTokenSigner implements TokenSigner {

    /**
     * Note: `NetworkManager` is meant to be a representation of an asynchronous
     * network class and is not provided as part of the SDK. It is a reference only.
     */
    NetworkManager network = new NetworkManager();

    @NotNull
    @Override
    public void sign(@NotNull String unsignedToken, @NotNull ResultCallback<String> callback) {

        network.post("https://www.example.com/sign", body: unsignedToken.getBytes("UTF-8"), new NetworkListener() {

            /**
             * Send a result success if the data received is formatted as expected.
             *
             * @param bytes The body of the response.
             */
            @NotNull
            @Override
            void success(byte[] bytes) {
                String signedToken = new String(bytes, "UTF-8");
                Result<String> result = Result.Companion.SUCCESS(signedToken);
                callback.execute(result);
            }

            /**
             * Send a result failure if there was a problem.
             *
             * @param t The reason for the failure.
             */
            @NotNull
            @Override
            void failure(Throwable t) {
                Result<String> result = Result.Companion.FAILURE(t);
                resultCallback.execute(result);
            }

        });
    }
}

Swift

class MyTokenSigner: TokenSigner {

    struct SigningError: Error {
        let message: String
    }

    let signingURL = URL(string: "http://{BACKEND-SIGNING-URL}")!

    func sign(_ unsignedToken: String, resultHandler: @escaping ResultHandler<String>) {
        var request = URLRequest(url: signingURL)
        request.httpMethod = "POST"
        request.httpBody = unsignedToken.data(using: .utf8)
        request.allHTTPHeaderFields = [
            "Authorization": "Bearer {APP-AUTHORIZATION}",
            "Content-Type": "text/plain"
        ]

        let task = URLSession.shared.dataTask(with: request) { data, response, error in
            guard let response = response as? HTTPURLResponse else {
                // send a failure result
                resultHandler(.failure(SigningError(message: "Invalid response type, expecting HTTPURLResponse")))
            }

            guard response.statusCode == 200 else {
                // send a failure result
                resultHandler(.failure(SigningError(message: "Bad response: \(response.statusCode)")))
                return
            }

            guard let data = data, let signedToken = String(data: data, encoding: .utf8) else {
                // Send a failure result
                resultHandler(.failure(SigningError(message: "Problem signing token")))
                return
            }

            // Send a success result if the data received is formatted as expected.
            resultHandler(.success(signedToken))
        }

        task.resume()
    }
}

iOS TokenSigner Initialization

The SDK receives the TokenSigner via a factory that is provided during the SDK initialization process. The SDK initialization must occur at the beginning of the host app's lifecycle before any other SDK functionality is invoked. You can see an example of the full initialization in the usage section of this document.

Swift
// Somewhere early in the app lifecycle...
initialize(/* other initializations... */, tokenSignerFactory: { MyTokenSigner() })

Android TokenSigner Configuration

The SDK receives the TokenSignerclass during the SDK configuration process. The token signer class must have a no-argument (empty) constructor, and must not be obfuscated. You can see an example in the Initialize the SDK section of this document.

if (!ImsSdkManager.isConfigured) {
    ImsSdkManager.setSdkConfiguration( context,
        ImsSdkManager.Builder()
            // Token signer class MUST have a no-argument constructor
            .setTokenSigner(MyTokenSigner::class.java)
            // Other configuration options
            .build() )
}

Signing Security Requirements

The following are security requirements for the host app to follow when signing tokens:

For additional security, please consider the following procedures:

Public Certificate and RSA Key Pair

The host is responsible for generating a 2048 bit RSA key pair and providing IMS with a public certificate generated from the key pair, signed using the SHA-256 with RSA algorithm. The generated private key should be used to sign the JWT prototypes.

Certificate Example

This is an example of a public certificate generated from a key pair that could be provided to IMS for JWT validation.

sample.cer

-----BEGIN CERTIFICATE-----
MIIDrjCCApagAwIBAgIEWUsVMTANBgkqhkiG9w0BAQsFADCBmDEdMBsGCSqGSIb3
DQEJARYOeW91ckBlbWFpbC5jb20xCzAJBgNVBAYTAkNBMRAwDgYDVQQIDAdPbnRh
cmlvMRIwEAYDVQQHDAlLaXRjaGVuZXIxGjAYBgNVBAoMEVlvdXIgT3JnYW5pemF0
aW9uMRQwEgYDVQQLDAtFbmdpbmVlcmluZzESMBAGA1UEAwwJWW91ciBOYW1lMB4X
DTE3MDYyMjAwNTUwMVoXDTE4MDYyMjAwNTUwMVowgZgxHTAbBgkqhkiG9w0BCQEW
DnlvdXJAZW1haWwuY29tMQswCQYDVQQGEwJDQTEQMA4GA1UECAwHT250YXJpbzES
MBAGA1UEBwwJS2l0Y2hlbmVyMRowGAYDVQQKDBFZb3VyIE9yZ2FuaXphdGlvbjEU
MBIGA1UECwwLRW5naW5lZXJpbmcxEjAQBgNVBAMMCVlvdXIgTmFtZTCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAOwEevBh2CzHIJLI94v/gsE9dH43WH+U
/YXGDGm3PTXOOtzca2YLbpGwikXgAePpTEqiH/vhLHYxMXuwhH8a2i3WJHfqV6fK
FBSceC4nHEJuL/KPgW5s2SSmZxHxiGoXaUm2v62HYzO0x8JfR9i3Z7cV+Hn7SBiT
6wOTQaD4+4tLfcnuvF93dzmqF466Bc1ZejPxJYOJrIPKcLA0CZuO1jeGbi/oBGNd
5hMuDAGxfSDBKuiXnJSEocoeSMpiswqs7p4F2vGfiPh9rfmwezPlEhTo/1TGJMzr
K7Iq79ru7a9dg0uiTvDcBWa7pIVvPmjwgHAeYK7l22zysqX0v5xV/+UCAwEAATAN
BgkqhkiG9w0BAQsFAAOCAQEAf/+mKOkC/D0ecRl+HDMJYAW3LPVC0awhRGnRsJtc
c0jB7wPejibsXp+r6aMmK7FCh/H8DKxi2YjO9WGO5N9B346LgzrBAIAt8qJh7Grt
EWG2VDcGTxaI/tLcXbCvZTJFi3Va+NynKYRmnuYhI86jazg7aAl3LwvbYPXZwOMI
zBTw9Izbz4ffnsNrEuY1E1zYF04juJTKZk411WfFaoA33UEhEPYWkfTJNraEmecm
Eaj8nuRPpio0+HAIEBfheDvsIcS7Tov96G8egIMxJAKkKolxL8QCkYhRf4ipztwr
dg7ZGZBMpdKSFQEl3He1A+fsUc3FJYjkMKtvsxRtzAIjIg==
-----END CERTIFICATE-----

TIP: The private/public key pair can be generated using a command line tool such as Java Keystore or any other tool that can generate the appropriate key pair and certificate.

Example HTTP Signing Request This is an example request to the host app's back end, requesting that an unsigned JWT prototype be validated and signed. This is only an example implementation. The actual implementation is left up to the host app, provided the requirements outlined above are followed.

Request

POST /sign HTTP/1.1
Host: www.example.com
Authorization: Bearer CLIENT_AUTHORIZATION_TOKEN
Content-Type: text/plain
Cache-Control: no-cache

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI2M2Q5YTM4OS00ZGRjLTRkZjEtOGRkMS1hZjZhMTBjNzIyNmUiLCJzdWIiOiIxNCIsImp0aSI6IkJEOTg4QjJFLUIxNkEtNDlCOC04ODgyLTlCM0M5RDM1REQ4NCIsIm5iZiI6MTQ5NzU3NzM4MSwiZXhwIjoxNDk3NTc4MjgxfQ
  1. The bearer token (CLIENT_AUTHORIZATION_TOKEN) in the above request is an example of an authentication token used by the app and the host's back end, not the SDK JWT. This token is used to determine the authenticity of the host's user.

  2. The body of the request is the JWT Prototype created by IMS SDK.

Response

HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI2M2Q5YTM4OS00ZGRjLTRkZjEtOGRkMS1hZjZhMTBjNzIyNmUiLCJzdWIiOiIxNCIsImp0aSI6IkJEOTg4QjJFLUIxNkEtNDlCOC04ODgyLTlCM0M5RDM1REQ4NCIsIm5iZiI6MTQ5NzU3NzM4MSwiZXhwIjoxNDk3NTc4MjgxfQ.HOST-BACKEND-SIGNATURE

IMPORTANT: The header and payload must not be modified in any way. The response body is the prototype token with the signature appended, as shown.

Last updated