{"_id":"5d7294199286390037dfd0d6","project":"55dd9f2e0efd5821000d54d9","version":{"_id":"55dd9f4dab0e4d210045aae9","__v":45,"project":"55dd9f2e0efd5821000d54d9","createdAt":"2015-08-26T11:13:17.024Z","releaseDate":"2015-08-26T11:13:17.024Z","categories":["55dd9f4dab0e4d210045aaea","55ddb5fa9067202b00ddff6f","55e0472c6bad670d0081f213","55e04764a44fae0d00214671","55e047a9a44fae0d00214672","55e047b258c5460d0076a9a7","55e95e337fc27b2d00d32cf2","55e979bda7ca823900ad549a","55edb8c18dcb210d0056900b","55f0365c8563861700a33765","55f03677d58f9b1900acf996","55f036938eeefc23001ea5de","55f036a38563861700a33767","55f036c08563861700a33769","55f036d02911b72100482cd7","55f036e92911b72100482cd9","55f036fa8563861700a3376b","55f0370ee507711900e58c69","55f0371df6101b1900c70700","55f0374f2911b72100482cdb","55f0375e2911b72100482cdc","560eb0f659cb8d0d0015cd52","560eb25239fad419002ae1e0","561fb64d4d67490d00804b2a","562b9f775a39cd0d009aff22","562ba0505a39cd0d009aff23","562ba149d56bc30d00f0cb18","562ba595f68a5f0d007b1f3b","562ba78fd56bc30d00f0cb1b","562ba8b95a39cd0d009aff27","562baadf6562140d001501d2","562bab37f68a5f0d007b1f3d","562bc1bf9ebc950d000f7523","562bc99ced4bea0d00c11dfa","562bd29c1b98640d00714520","562bd5875a39cd0d009aff60","562bdfabff2da50d002c0aaf","562be0bd5a39cd0d009aff75","57a0b476d8313e1900454439","5b19051beece890003020163","5b34ded01cb20f000391ad6d","5b3a325acffe770003fd29e5","5b3c737a7f7b890003365501","5b3c929b367036000391b11e","5b7c1e210dc2e20003871521"],"is_deprecated":false,"is_hidden":false,"is_beta":false,"is_stable":true,"codename":"","version_clean":"2.0.0","version":"2"},"category":{"_id":"5b34ded01cb20f000391ad6d","project":"55dd9f2e0efd5821000d54d9","version":"55dd9f4dab0e4d210045aae9","__v":0,"sync":{"url":"","isSync":false},"reference":false,"createdAt":"2018-06-28T13:12:48.898Z","from_sync":false,"order":1,"slug":"push-notifications","title":"Push notifications"},"user":"55114030a226890d00911658","__v":0,"parentDoc":null,"updates":[],"next":{"pages":[],"description":""},"createdAt":"2019-09-06T17:15:05.449Z","link_external":false,"link_url":"","sync_unique":"","hidden":false,"api":{"settings":"","results":{"codes":[]},"auth":"required","params":[],"url":""},"isReference":false,"order":8,"body":"Our mobile SDKs use [JSON Web Tokens (JWT)](https://jwt.io/) to authenticate your app. These are a commonly used method for providing authentication and authorisation using one simple token. \n\nIn your code, you'll need to create a function that generates a valid JWT using the JWT authorisation details provided by Engagement Cloud in your [push notification profile](doc:creating-a-push-notification-profile-new), and a cryptographic nonce which we pass. We will refer to this as the *challenge* function.\n\n## Required JWT Claims\nWhichever provider or method you use to create a JWT, the JWT's claims must include the following with values provided in the *Authentication* fields in your [push notification profile](doc:creating-a-push-notification-profile-new) you setup in Engagement Cloud:\n[block:parameters]\n{\n  \"data\": {\n    \"h-0\": \"JWT Claim\",\n    \"h-1\": \"Value\",\n    \"h-2\": \"Value\",\n    \"0-0\": \"`iss`\",\n    \"1-0\": \"`aud`\",\n    \"2-0\": \"`sub`\",\n    \"0-1\": \"The **Issuer** value in your Engagement Cloud push notification profile\\ne.g. *https://api.comapi.com/defaultauth*\",\n    \"1-1\": \"The **Audience** value in your Engagement Cloud push notification profile\\ne.g. *https://api.comapi.com*\",\n    \"2-1\": \"A unique id for the user, such as a GUID\\ne.g. *f0cf444d-4237-4ece-9882-8e6ccc0a3b7d*\",\n    \"2-2\": \"\",\n    \"1-2\": \"\",\n    \"0-2\": \"\"\n  },\n  \"cols\": 2,\n  \"rows\": 3\n}\n[/block]\n## Signing the JWT\nJWTs are digitally signed to prove it was issued by a trusted party. You must sign the JWT with the value of the **Shared secret** field from your [push notification profile](doc:creating-a-push-notification-profile-new) and the nonce we pass your function to prove it was issued by you.\n\n### Nonces\nA cryptographic nonce must also be used by your JWT provider when creating the token to ensure that tokens always appear different even if they contain the same claims, this is a common security practice with cryptography. The SDK passes this `nonce` as an argument to your challenge function during initialisation of the SDK where you will create or retrieve a JWT for your app user.\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://files.readme.io/eca3883-Channel_management_push_notification_profile_-_Authentication-shared_secret.png\",\n        \"Channel management_push notification profile - Authentication-shared secret.png\",\n        600,\n        262,\n        \"#e7e3e2\"\n      ]\n    }\n  ]\n}\n[/block]\n\n[block:callout]\n{\n  \"type\": \"danger\",\n  \"title\": \"Important security note\",\n  \"body\": \"We recommend that you do not hard code your shared secret on the client side. Instead, this value should be stored on the server side.\"\n}\n[/block]\n# Sample Code\nThe following example code demonstrates how to create a *challenge* function and a self issued JWT for both Android and iOS:\n\n## Android JWT code sample\n\n*JWT provider:* [Java JWT](https://github.com/jwtk/jjwt).\n\nAny class that you create to generate a JWT token must extend the `ComapiAuthenticator` class.\n\nAny logic that creates the JWT token must be inside the `onAuthenticationChallenge()` method.\nThis method takes two parameters: `AuthClient` and `ChallengeOptions`.\n\nGet the value of the nonce by calling the `ChallengeOptions.GetNonce()` method\n\nWhen your JWT token is returned, pass it to the `authenticateWithToken()` method of the `AuthClient` object.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"package com.example.testapp;\\n\\nimport android.content.SharedPreferences;\\nimport android.text.TextUtils;\\nimport android.util.Base64;\\n\\nimport com.comapi.ComapiAuthenticator;\\nimport com.comapi.internal.network.AuthClient;\\nimport com.comapi.internal.network.ChallengeOptions;\\n\\nimport java.io.UnsupportedEncodingException;\\nimport java.util.HashMap;\\nimport java.util.Map;\\nimport java.util.concurrent.TimeUnit;\\n\\nimport io.jsonwebtoken.Jwts;\\nimport io.jsonwebtoken.SignatureAlgorithm;\\n\\npublic class ChallengeHandler extends ComapiAuthenticator {\\n\\n\\n    :::at:::Override\\n    public void onAuthenticationChallenge(AuthClient authClient, ChallengeOptions challengeOptions) {\\n\\n      try {\\n\\n        byte[] data;\\n        //<Shared secret> string must be the same as the value of the 'Shared secret' field in your push notification profile in Engagement Cloud. \\n        data = \\\"<Shared secret>\\\".getBytes(\\\"UTF-8\\\");\\n\\n        String base64Secret = Base64.encodeToString(data, Base64.DEFAULT);\\n\\n        Map<String, Object> header = new HashMap<>();\\n        header.put(\\\"typ\\\", \\\"JWT\\\");\\n\\n        Map<String, Object> claims = new HashMap<>();\\n        claims.put(\\\"nonce\\\", challengeOptions.getNonce());\\n        //<ID claim> string must be the same as the value of the 'ID claim' field in your push notification profile in Engagement Cloud.\\n        claims.put(\\\"sub\\\", \\\"<ID claim>\\\");\\n        //<Audience> string must be the same as the value of the 'Audience' field in your push notification profile in Engagement Cloud.\\n        claims.put(\\\"aud\\\", \\\"<Audience>\\\");\\n        //<Issuer> string must be the same as the value of the 'Issuer' field in your push notification profile in Engagement Cloud.\\n        claims.put(\\\"iss\\\", \\\"<Issuer>\\\");\\n        claims.put(\\\"iat\\\", System.currentTimeMillis());\\n        claims.put(\\\"exp\\\", System.currentTimeMillis() + TimeUnit.DAYS.toMillis(30));\\n\\n        final String token = Jwts.builder()\\n          .setHeader(header)\\n          .setClaims(claims)\\n          .signWith(SignatureAlgorithm.HS256, base64Secret)\\n          .compact();\\n\\n        //Pass the JWT token to the SDK.\\n        authClient.authenticateWithToken(token);\\n\\n      } catch (UnsupportedEncodingException e) {\\n        e.printStackTrace();\\n        //Authorisation failed.\\n        authClient.authenticateWithToken(null);\\n      }\\n    }\\n}\",\n      \"language\": \"java\",\n      \"name\": \"ChallengeHandler sample class\"\n    }\n  ]\n}\n[/block]\n## iOS JWT code sample\n\n*JWT provider:* ['Swifty JWT' framework](https://github.com/Wstunes/SwiftyJWT).\n\nAny class that you create to generate a JWT token must be a subclass of the `AuthenticationDelegate` superclass.\n\nGet the value of the nonce by using the `authenticationChallenge.nonce` property.\n\nWhen your JWT token is returned, pass it to the `continueWithToken()` method.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"import SwiftyJWT\\n\\nclass ChallengeHandler: AuthenticationDelegate {\\n    \\n    func client(_ client: ComapiClient,\\n                didReceiveAuthenticationChallenge authenticationChallenge: AuthenticationChallenge,\\n                completion continueWithToken: @escaping (String?) -> Void) {\\n\\nlet alg = JWTAlgorithm.hs256(\\\"<Shared secret>\\\")\\nvar payload = JWTPayload()\\npayload.issuer = \\\"<Issuer>\\\"\\npayload.subject = \\\"<ID Claim>\\\"\\npayload.audience = \\\"<Audience>\\\"\\npayload.nonce = authenticationChallenge.nonce\\n\\nlet token = try? JWT.init(payload: payload, algorithm: alg)\\n\\n                  continueWithToken(token)\\n\\t\\t}\\n}\",\n      \"language\": \"swift\",\n      \"name\": \"ChallengeHandler sample class\"\n    }\n  ]\n}\n[/block]\n## JavaScript JWT code sample\n\n*JWT provider:* [jsrassign](https://kjur.github.io/jsrsasign/) library.\n\nGet the value of the nonce by using the `nonce` property of the first parameter.\n\nWhen your JWT token is returned, pass it to the `answerAuthenticationChallenge()` function (second parameter of your `challengeHandler()` function).\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"function challengeHandler (options, answerAuthenticationChallenge) {\\n    // Header\\n    var oHeader = { alg: 'HS256', typ: 'JWT' };\\n    // Payload\\n    var tNow = KJUR.jws.IntDate.get('now');\\n    var tEnd = KJUR.jws.IntDate.get('now + 1day');\\n    var oPayload = {\\n      \\t//<ID claim> string must be the same as the value of the 'ID claim' field in your push notification profile in Engagement Cloud.\\n        sub: \\\"<ID Claim>\\\",\\n        nonce: options.nonce,\\n      \\t//<Audience> string must be the same as the value of the 'Audience' field in your push notification profile in Engagement Cloud.\\n        aud: \\\"<Audience>\\\",\\n      //<Issuer> string must be the same as the value of the 'Issuer' field in your push notification profile in Engagement Cloud.\\n        iss: \\\"<Issuer>\\\",\\n        iat: tNow,\\n        exp: tEnd,\\n    };\\n    var sHeader = JSON.stringify(oHeader);\\n    var sPayload = JSON.stringify(oPayload);\\n  \\t//<Shared secret> string must be the same as the value of the 'Shared secret' field in your push notification profile in Engagement Cloud. \\n    var sJWT = KJUR.jws.JWS.sign(\\\"HS256\\\", sHeader, sPayload, \\\"<Shared secret>\\\");\\n    answerAuthenticationChallenge(sJWT);\\n}\",\n      \"language\": \"javascript\",\n      \"name\": \"challengeHandler sample function\"\n    }\n  ]\n}\n[/block]","excerpt":"A tutorial for creating a function or class that generates a JWT for authorising your app to receive push notifications from Engagement Cloud","slug":"creating-a-json-web-token-new","type":"basic","title":"Creating a JWT"}

Creating a JWT

A tutorial for creating a function or class that generates a JWT for authorising your app to receive push notifications from Engagement Cloud

Our mobile SDKs use [JSON Web Tokens (JWT)](https://jwt.io/) to authenticate your app. These are a commonly used method for providing authentication and authorisation using one simple token. In your code, you'll need to create a function that generates a valid JWT using the JWT authorisation details provided by Engagement Cloud in your [push notification profile](doc:creating-a-push-notification-profile-new), and a cryptographic nonce which we pass. We will refer to this as the *challenge* function. ## Required JWT Claims Whichever provider or method you use to create a JWT, the JWT's claims must include the following with values provided in the *Authentication* fields in your [push notification profile](doc:creating-a-push-notification-profile-new) you setup in Engagement Cloud: [block:parameters] { "data": { "h-0": "JWT Claim", "h-1": "Value", "h-2": "Value", "0-0": "`iss`", "1-0": "`aud`", "2-0": "`sub`", "0-1": "The **Issuer** value in your Engagement Cloud push notification profile\ne.g. *https://api.comapi.com/defaultauth*", "1-1": "The **Audience** value in your Engagement Cloud push notification profile\ne.g. *https://api.comapi.com*", "2-1": "A unique id for the user, such as a GUID\ne.g. *f0cf444d-4237-4ece-9882-8e6ccc0a3b7d*", "2-2": "", "1-2": "", "0-2": "" }, "cols": 2, "rows": 3 } [/block] ## Signing the JWT JWTs are digitally signed to prove it was issued by a trusted party. You must sign the JWT with the value of the **Shared secret** field from your [push notification profile](doc:creating-a-push-notification-profile-new) and the nonce we pass your function to prove it was issued by you. ### Nonces A cryptographic nonce must also be used by your JWT provider when creating the token to ensure that tokens always appear different even if they contain the same claims, this is a common security practice with cryptography. The SDK passes this `nonce` as an argument to your challenge function during initialisation of the SDK where you will create or retrieve a JWT for your app user. [block:image] { "images": [ { "image": [ "https://files.readme.io/eca3883-Channel_management_push_notification_profile_-_Authentication-shared_secret.png", "Channel management_push notification profile - Authentication-shared secret.png", 600, 262, "#e7e3e2" ] } ] } [/block] [block:callout] { "type": "danger", "title": "Important security note", "body": "We recommend that you do not hard code your shared secret on the client side. Instead, this value should be stored on the server side." } [/block] # Sample Code The following example code demonstrates how to create a *challenge* function and a self issued JWT for both Android and iOS: ## Android JWT code sample *JWT provider:* [Java JWT](https://github.com/jwtk/jjwt). Any class that you create to generate a JWT token must extend the `ComapiAuthenticator` class. Any logic that creates the JWT token must be inside the `onAuthenticationChallenge()` method. This method takes two parameters: `AuthClient` and `ChallengeOptions`. Get the value of the nonce by calling the `ChallengeOptions.GetNonce()` method When your JWT token is returned, pass it to the `authenticateWithToken()` method of the `AuthClient` object. [block:code] { "codes": [ { "code": "package com.example.testapp;\n\nimport android.content.SharedPreferences;\nimport android.text.TextUtils;\nimport android.util.Base64;\n\nimport com.comapi.ComapiAuthenticator;\nimport com.comapi.internal.network.AuthClient;\nimport com.comapi.internal.network.ChallengeOptions;\n\nimport java.io.UnsupportedEncodingException;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.TimeUnit;\n\nimport io.jsonwebtoken.Jwts;\nimport io.jsonwebtoken.SignatureAlgorithm;\n\npublic class ChallengeHandler extends ComapiAuthenticator {\n\n\n @Override\n public void onAuthenticationChallenge(AuthClient authClient, ChallengeOptions challengeOptions) {\n\n try {\n\n byte[] data;\n //<Shared secret> string must be the same as the value of the 'Shared secret' field in your push notification profile in Engagement Cloud. \n data = \"<Shared secret>\".getBytes(\"UTF-8\");\n\n String base64Secret = Base64.encodeToString(data, Base64.DEFAULT);\n\n Map<String, Object> header = new HashMap<>();\n header.put(\"typ\", \"JWT\");\n\n Map<String, Object> claims = new HashMap<>();\n claims.put(\"nonce\", challengeOptions.getNonce());\n //<ID claim> string must be the same as the value of the 'ID claim' field in your push notification profile in Engagement Cloud.\n claims.put(\"sub\", \"<ID claim>\");\n //<Audience> string must be the same as the value of the 'Audience' field in your push notification profile in Engagement Cloud.\n claims.put(\"aud\", \"<Audience>\");\n //<Issuer> string must be the same as the value of the 'Issuer' field in your push notification profile in Engagement Cloud.\n claims.put(\"iss\", \"<Issuer>\");\n claims.put(\"iat\", System.currentTimeMillis());\n claims.put(\"exp\", System.currentTimeMillis() + TimeUnit.DAYS.toMillis(30));\n\n final String token = Jwts.builder()\n .setHeader(header)\n .setClaims(claims)\n .signWith(SignatureAlgorithm.HS256, base64Secret)\n .compact();\n\n //Pass the JWT token to the SDK.\n authClient.authenticateWithToken(token);\n\n } catch (UnsupportedEncodingException e) {\n e.printStackTrace();\n //Authorisation failed.\n authClient.authenticateWithToken(null);\n }\n }\n}", "language": "java", "name": "ChallengeHandler sample class" } ] } [/block] ## iOS JWT code sample *JWT provider:* ['Swifty JWT' framework](https://github.com/Wstunes/SwiftyJWT). Any class that you create to generate a JWT token must be a subclass of the `AuthenticationDelegate` superclass. Get the value of the nonce by using the `authenticationChallenge.nonce` property. When your JWT token is returned, pass it to the `continueWithToken()` method. [block:code] { "codes": [ { "code": "import SwiftyJWT\n\nclass ChallengeHandler: AuthenticationDelegate {\n \n func client(_ client: ComapiClient,\n didReceiveAuthenticationChallenge authenticationChallenge: AuthenticationChallenge,\n completion continueWithToken: @escaping (String?) -> Void) {\n\nlet alg = JWTAlgorithm.hs256(\"<Shared secret>\")\nvar payload = JWTPayload()\npayload.issuer = \"<Issuer>\"\npayload.subject = \"<ID Claim>\"\npayload.audience = \"<Audience>\"\npayload.nonce = authenticationChallenge.nonce\n\nlet token = try? JWT.init(payload: payload, algorithm: alg)\n\n continueWithToken(token)\n\t\t}\n}", "language": "swift", "name": "ChallengeHandler sample class" } ] } [/block] ## JavaScript JWT code sample *JWT provider:* [jsrassign](https://kjur.github.io/jsrsasign/) library. Get the value of the nonce by using the `nonce` property of the first parameter. When your JWT token is returned, pass it to the `answerAuthenticationChallenge()` function (second parameter of your `challengeHandler()` function). [block:code] { "codes": [ { "code": "function challengeHandler (options, answerAuthenticationChallenge) {\n // Header\n var oHeader = { alg: 'HS256', typ: 'JWT' };\n // Payload\n var tNow = KJUR.jws.IntDate.get('now');\n var tEnd = KJUR.jws.IntDate.get('now + 1day');\n var oPayload = {\n \t//<ID claim> string must be the same as the value of the 'ID claim' field in your push notification profile in Engagement Cloud.\n sub: \"<ID Claim>\",\n nonce: options.nonce,\n \t//<Audience> string must be the same as the value of the 'Audience' field in your push notification profile in Engagement Cloud.\n aud: \"<Audience>\",\n //<Issuer> string must be the same as the value of the 'Issuer' field in your push notification profile in Engagement Cloud.\n iss: \"<Issuer>\",\n iat: tNow,\n exp: tEnd,\n };\n var sHeader = JSON.stringify(oHeader);\n var sPayload = JSON.stringify(oPayload);\n \t//<Shared secret> string must be the same as the value of the 'Shared secret' field in your push notification profile in Engagement Cloud. \n var sJWT = KJUR.jws.JWS.sign(\"HS256\", sHeader, sPayload, \"<Shared secret>\");\n answerAuthenticationChallenge(sJWT);\n}", "language": "javascript", "name": "challengeHandler sample function" } ] } [/block]