Developer Console

SSI Token Library

The SSI token library is a Java library containing utility classes to generate and validate link and SSI tokens. You can use this library to perform the following actions.

  • Generate link tokens to issue to Amazon during account linking setup.
  • Validate and decode SSI tokens during new sign-in requests to your app.
  • Validate and decode link tokens during new sign-in requests to your app; a link token is extracted by decoding an SSI token.
  • Generate SSI tokens to mimic the behavior of the Amazon SSI server in your internal tests.

To use the SSI token library in your project, add the JAR file, SimpleSignInTokenCommon-<x.y>.jar. You can download the JAR here, or get it from the libs folder of the SSI sample app, included in the Appstore SDK. This JAR file will be a dependency in your project. For other direct and transitive Maven dependencies, see the build.gradle file in the SSI sample app.

The following sections explain the library usage for the preceding actions, with sample code references. For a more detailed description of individual classes and methods in Javadoc format, see the full SSI Token Library API reference.

The LinkTokenV1Provider class provides the APIs to generate link tokens. The constructor used to instantiate LinkTokenV1Provider takes two input parameters - instances of your implementations of the ILinkTokenCryptoKeyProvider and IAppStorePublicKeyProvider interfaces. These implementations include the logic to retrieve cryptographic keys from your key store.

The logic to retrieve cryptographic keys and the public key pair is inside the ILinkTokenCryptoKeyProvider and IAppStorePublicKeyProvider interfaces. Use the following code to create a LinkTokenV1Provider object, which is used to generate link tokens.

LinkTokenV1Provider linkTokenV1Provider =
  new LinkTokenV1Provider(
    new LinkTokenCryptoKeyProvider(), new AppStorePublicKeyProvider());

Generate and cache your key

The following sample implementation of ILinkTokenCryptoKeyProvider generates and caches an AES 128-bit key during instantiation of the class and provides this key any time it's requested. In a real implementation, you must add logic to fetch the keys from a secure store where your app keys are pre-created and stored.

 static class LinkTokenCryptoKeyProvider implements ILinkTokenCryptoKeyProvider {
   SimpleSignInCryptoKey < SecretKey > cryptoKey;

   LinkTokenCryptoKeyProvider() throws TokenException {
     try {
       KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
       keyGenerator.init(128, new SecureRandom());
       SecretKey secretKey = keyGenerator.generateKey();
       cryptoKey = new SimpleSignInCryptoKey(secretKey, "key.1");
     } catch (NoSuchAlgorithmException e) {
       throw new TokenException(e);
     }
   }

   @Override
   public SimpleSignInCryptoKey < SecretKey > getEncryptionKey(IRequestContext iRequestContext) throws TokenException {
     /*
     Return the encryption key to use for a given application revision. Developers who own 
     multiple applications with each application having multiple revisions, can use
     IRequestContext object to pass relevant context about the request origin and
     pick the appropriate key to use. 
      */
     return cryptoKey;
   }

   @Override
   public SimpleSignInCryptoKey < SecretKey > getDecryptionKey(IRequestContext iRequestContext, String s) throws TokenException {
     /*
     Return the decryption key to use for a given application revision. Developers who own 
     multiple applications with each application having multiple revisions, can use
     IRequestContext object to pass relevant context about the request origin and
     pick the appropriate key to use. 
      */
     return cryptoKey;
   }
 }

Generate static key pairs

The following two code samples used to implement IAppStorePublicKeyProvider, generate a static key pair locally and expose the public key component of the generated key pair through its getPublicKey() method. In the actual implementation, you must add logic to fetch key material from a secure store where the public key assigned by Appstore is stored.

private static KeyPair appStoreKeyPair;

static {
  KeyPairGenerator keyPairGenerator;
  try {
    keyPairGenerator = KeyPairGenerator.getInstance("RSA");
  } catch (NoSuchAlgorithmException ex) {
    throw new RuntimeException(ex);
  }
  keyPairGenerator.initialize(2048);
  appStoreKeyPair = keyPairGenerator.generateKeyPair();
}
 static class AppStorePublicKeyProvider implements IAppStorePublicKeyProvider {
   @Override
   public SimpleSignInCryptoKey < PublicKey > getPublicKey(IRequestContext iRequestContext)
   throws AppStorePublicKeyException {
     /*
     The KeyIdentifier "key.1" is hard-coded here. In the actual implementation, developers would have
     added relevant details about the request origin (i.e., application and its version) into
     the IRequestContext object. These details will be used to determine the key identifier
     and hence the appropriate public key to use.
      */
     SimpleSignInCryptoKey < PublicKey > appStorePublicKey =
       new SimpleSignInCryptoKey < > (appStoreKeyPair.getPublic(), "key.1");
     return appStorePublicKey;
   }
 }

Now, you can generate a link token by invoking the generateLinkToken() method. The request object of type GenerateLinkTokenV1Request contains the details to be encoded inside the token.

GenerateLinkTokenV1Request request = GenerateLinkTokenV1Request.builder()
  .directedAmazonUserId("amazonUserId")
  .partnerUserId("partnerUserId")
  .customFields(null) //Map of custom fields
  .requestContext(requestContext)
  .build();
LinkTokenContainer linkTokenContainer = linkTokenV1Provider.generateLinkToken(request);

The LinkTokenContainer object returned as an output contains the following data.

  • token: The link token payload. This is the plain string that captures the identity of a user within your system. It's used as proof of linkage with an Amazon user.
  • tokenSchema: The link token schema, LINK-TOKEN-1.0.
  • linkSigningKeyEncrypted: The link signing key. An EC private key for which the SSI tokens for this link should be signed. Encrypted using your Appstore public key.

Validate and decode SSI token

To validate and decode the SSI token, use the decodeAndVerifyToken() method of the SSITokenV1Validator class. This method verifies the signature of the SSI token and ensures the token isn't expired.

SSITokenV1Validator ssiTokenV1Validator
  = new SSITokenV1Validator(linkTokenV1Provider);
SSITokenInfo ssiTokenInfo = ssiTokenV1Validator
  .decodeAndVerifyToken(ssiToken, requestContext);

The input parameters used in this example are:

  • linkTokenV1Provider - A LinkTokenV1Provider object passed into the constructor of SSITokenV1Validator. This is the same instance of LinkTokenV1Provider that you set up for link token generation.
  • requestContext - An instance of your implementation of the IRequestContext interface. This object contains the details needed for your implementation of ILinkTokenCryptoKeyProvider to get to the appropriate decryption key, to decrypt the link token, and to extract a link verification key. A link verification key is used to verify the SSI token signature embedded inside it.
  • ssiToken - The SSIToken object that you want to decode and verify.

SSI tokens

SSI tokens have the format SSIToken(token=<xxxxx.yyyyy.zzzzz>, schema=SSI-TOKEN-1.0). The token here is a JSON Web Token (JWT), which typically looks like "xxxxx.yyyyy.zzzzz". Where xxxxx is the Base64Url encoded header, yyyyy is the Base64Url encoded payload, and zzzzz is the computed signature of xxxxx and yyyyy, with an app specific private key.

A decoded SSI token has the following format:

SSITokenInfo(linkInfo=SSITokenInfo.LinkInfo(directedAmazonUserId=amazonUserId,
directedPartnerUserId=partnerUserId,
linkToken=LinkToken(token=<A plain string link token that captures the identity
of a user within your system and proves linkage with an Amazon user. This token 
string was passed during the account linking request>,tokenSchema=LINK-TOKEN-1.0)),
tokenMetadata=SSITokenV1Metadata(super=SSITokenMetadata(issuedAt=1628082286000,
notValidBefore=1628082286000, expiresAt=1628082586000),issuer=http://ssi.amazon.com, 
audience=TEST_DEVELOPER_ID,jwtId=66141c38-1bb5-4336-a2a6-55247345dc99))

Once an SSI token is successfully validated, validate the wrapped link token and extract customer identification details from the token. Use the validateLinkToken() method of LinkTokenV1Provider to achieve this.

LinkTokenInfo linkTokenInfoFromSSIToken = linkTokenV1Provider
  .validateLinkToken(ssiTokenInfo, requestContext);

The input parameters to the validateLinkToken() method used in this example are:

  • ssiTokenInfo - An instance of the SSITokenInfo type, which contains the details decoded from an SSI Token.
  • requestContext - An instance of your implementation of the IRequestContext interface. This object contains the details needed in your implementation of ILinkTokenCryptoKeyProvider to get the decryption key used to decrypt the link token.

Generate an SSI token

You don't need to generate SSI tokens yourself for production apps, but you might want to generate tokens for testing purposes. You can generate SSI tokens with the SSI token library, following these steps:

  1. Generate a link token by following the steps described in Generate a link token.
  2. Assume for your test app you have access to both private and public keys of the Appstore key pair. Although, for a production app, you'll have access to the public key only. Implement the IAppStorePrivateKeyProvider interface to enable access to the private key to decrypt the link signing key, which is issued along with the link token and encrypted using the Appstore public key.

    The following sample implementation of the IAppStorePrivateKeyProvider interface generates a static key pair locally and exposes the private key through its getPrivateKey() method. Use both code samples.

     private static KeyPair appStoreKeyPair;
    
     static {
       KeyPairGenerator keyPairGenerator;
       try {
         keyPairGenerator = KeyPairGenerator.getInstance("RSA");
       } catch (NoSuchAlgorithmException ex) {
         throw new RuntimeException(ex);
       }
       keyPairGenerator.initialize(2048);
       appStoreKeyPair = keyPairGenerator.generateKeyPair();
     }
    
     static class AppStorePrivateKeyProvider implements IAppStorePrivateKeyProvider {
       @Override
       public SimpleSignInCryptoKey < PrivateKey > getPrivateKey(String app, String appVersion) throws AppStorePrivateKeyException {
        SimpleSignInCryptoKey < PrivateKey > appStorePrivateKey =
          new SimpleSignInCryptoKey < > (appStoreKeyPair.getPrivate(), "key.1");
         return appStorePrivateKey;
       }
     }
    
  3. Decrypt the link signing key using a LinkSigningKeyDecryptor object. The LinkSigningKeyDecryptor instance is built using your implementation of the IAppStorePrivateKeyProvider interface.

     LinkSigningKeyDecryptor linkSigningKeyDecryptor =
       new LinkSigningKeyDecryptor(new AppStorePrivateKeyProvider());
     String linkSigningKey = linkSigningKeyDecryptor
       .decryptLinkSigningKey(linkTokenContainer.getLinkSigningKeyEncrypted(),
         "TEST_ASIN", "TEST_APP_VERSION");
    
  4. Generate an Amazon representation of the account linking relationship by building a Link object as follows.

     Link link = Link.builder()
       .linkId("amzn1.ssi.link.123456")
       .directedAmazonUserId("amazonUserId")
       .directedPartnerUserId("partnerUserId")
       .identityProvider("partnerIdp")
       .linkedTime(System.currentTimeMillis())
       .linkSigningKey(linkSigningKey)
       .linkToken(linkTokenContainer.getLinkToken())
       .build();
    
  5. Generate an SSI token for the linked account represented using the Link object created in the previous step. Use the generateToken() API exposed through the SSITokenV1Generator class to create an SSI token as follows.
SSITokenV1Generator ssiTokenV1Generator = new SSITokenV1Generator();
ProductInfo productInfo = ProductInfo.builder()
  .vendorId("TEST_DEVELOPER_ID")
  .asin("TEST_ASIN")
  .productVersion("TEST_APP_VERSION")
  .build();
SSIToken ssiToken = ssiTokenV1Generator.generateToken(link, productInfo);

Last updated: Mar 13, 2023