# Issue channel access tokens v2.1

This guide describes the Assertion Signing Key specification, how to generate a JSON Web Token (JWT) from the signing key, and how to issue a channel access token v2.1 using the generated JWT.

# Diagram for issuing a channel access token v2.1

This diagram shows these three steps:

Procedure for issuing a channel access token

On the channel access token v2.1 specification

The authentication method for issuing a channel access token v2.1 is in accordance with Using JWTs as Authorization Grants(RFC 7523) (opens new window). This is the Assertion Framework of OAuth Assertion Framework(RFC 7521) (opens new window) using JSON Web Token(RFC 7519) (opens new window).

# Create an Assertion Signing Key

Issuing an Assertion Signing Key is done in the these two steps:

# 1. Generate a key pair for the Assertion Signing Key

In order to generate a JWT, you must first generate a key pair (private key, public key) for the Assertion Signing Key.

# Assertion Signing Key specification

You can use a JSON Web Key(RFC7517) (opens new window) that meets these criteria as an Assertion Signing Key for JWT.

  1. Must be an RSA public key (RSA is configured in the ktyproperty).
  2. RSA key length is 2048bit.
  3. RS256 (RSASSA-PKCS1-v1_5 with SHA256) is used in the signing algorithm (RS256 is configured in the alg property).
  4. Must state that the public key will be used for signing (appropriate value configured in either use or key_ops).

Therefore, the public key of the Assertion Signing Key must contain these fields:

Property Description
kty Cryptographic algorithm family used in key. Specify RSA.
alg Algorithm used in key. Specify RS256.
use Use of key. Specify sig.*1
key_ops Operation where key is expected to be used. Specify ["verify"]. Exclude elements other than verify. *1
e Absolute value for restoring public key.
n Cryptographic index for restoring public key.

*1 Valid if one of either use or key_ops is specified.

An error will occur if the public key of the Assertion Signing Key to be registered contains a kid

kid is issued when a public key is registered in the LINE Developers Console. If the public key contains kid, an error will occur. Make sure the public key you are about to register doesn't contain kid.

The key pair for the Assertion Signing Key can be generated by the developer's own program based on the published specification, but it can be generated more easily by using a library that meets the specification.

Here is an example of the steps for generating an Assertion Signing Key:

# Generate using Go language library jwx

You can generate a key pair using jwx command line tool (opens new window), which is a part of jwx (opens new window), an open source Go language library used for implementing JWT. Use these steps to issue an Assertion Signing Key.

# 1. Install jwx command line tool

To install the jwx command line tool, a Go language development environment is required beforehand. You can download the development environment from the Go language official site (opens new window).

Execute this command from the path where you installed Go to install the jwx command line tool.

$ go get github.com/lestrrat-go/jwx/cmd/jwx
# 2. Generate private key and public key

Use the same path to execute this command and generate a private key.

$ ./jwx jwk generate --type RSA --keysize 2048 --template '{"alg":"RS256","use":"sig"}' > private.key

Use this command to generate a public key based on the private key.

$ ./jwx jwk format --public-key private.key > public.key

If you succeed, a private key (private.key) and public key (public.key) will be generated as below.

private.key example:

{
  "alg": "RS256",
  "d": "JeSJWnvZ......",
  "dp": "gBDRXGg7......",
  "dq": "MjFJ4xM9......",
  "e": "AQ......",
  "kty": "RSA",
  "n": "pTS2jGso......",
  "p": "xQibzkW6......",
  "q": "1qWtyQ9s......",
  "qi": "sdVGblc......",
  "use": "sig"
}

public.key example:

{
  "alg": "RS256",
  "e": "AQ......",
  "kty": "RSA",
  "n": "pTS2jGso......",
  "use": "sig"
}

# Generate using Python library jwcrypto

You can easily generate an Assertion Signing Key using the open source Python library, JWCrypto (opens new window), which is used for implementing JWT. Issue an Assertion Signing Key by following these steps.

# 1. Install JWCrypto

To install JWCrypto, you will need a Python 3 development environment and pip, a package manager. You can download the Python3 development environment from the Python official site (opens new window). Download the appropriate installer for your OS and install it.

Pip is installed at the same time that Python3 is installed. If pip isn't installed in your environment, refer to the pip documentation (opens new window) and install it separately.

Execute this command to install JWCrypto.

$ pip install jwcrypto
# 2. Create a code for generating private and public keys.

Create a python file that generates private and public keys by specifying kty as RSA, alg as RS256, use as sig, and size as 2048, like below.

Save the python file using any file name you want. In this case, the file name is app.py.

from jwcrypto import jwk
import json
key = jwk.JWK.generate(kty='RSA', alg='RS256', use='sig', size=2048)

private_key = key.export_private()
public_key = key.export_public()

print("=== private key ===\n"+json.dumps(json.loads(private_key),indent=2))
print("=== public key ===\n"+json.dumps(json.loads(public_key),indent=2))

In the same directory where you saved the python file, run the following command to generate a public key based on the private key.

$ python app.py

If successful, the following private key (private_key) and public key (public_key) will be generated as standard output.

private_key example:

{
  "alg": "RS256",
  "d": "zKh7iwIIPXXFKYQS...",
  "dp": "u1qKg_43UeuGpZFI...",
  "dq": "69AzYgpcg0ckypUrv...",
  "e": "AQ..",
  "kty": "RSA",
  "n": "_RzHf7cgG_i6Pdo_...",
  "p": "_20iRavoSrMIwWuRPxo...",
  "q": "_a5QodMBbEriAgztXvHi...",
  "qi": "JozdjTtK57IFLeVAB...",
  "use": "sig"
}

public_key example:

{
  "alg": "RS256",
  "e": "AQAB",
  "kty": "RSA",
  "n": "_RzHf7cgG_i6Pdo...",
  "use": "sig"
}

# Generate using browser

If your browser supports Web Crypto API (opens new window), you can use the SubtleCrypto.generateKey() (opens new window) method to generate a private key and public key.

For example, in the case of the Google Chrome browser, open the browser's Developer Tools, enter the following code into the JavaScript console and execute it.

(async () => {
  const pair = await crypto.subtle.generateKey(
    {
      name: 'RSASSA-PKCS1-v1_5',
      modulusLength: 2048,
      publicExponent: new Uint8Array([1, 0, 1]),
      hash: 'SHA-256'
    },
    true,
    ['sign', 'verify']
  );
   
  console.log('=== private key ===');
  console.log(JSON.stringify(await crypto.subtle.exportKey('jwk', pair.privateKey), null, '  '));
   
  console.log('=== public key ===');
  console.log(JSON.stringify(await crypto.subtle.exportKey('jwk', pair.publicKey), null, '  ')); 
})();

If you succeed, the following private key (private.key) and public key (public.key) are generated.

private.key example:

{
  "alg": "RS256",
  "d": "GaDzOmc4......",
  "dp": "WAByrYmh......",
  "dq": "WLwjYun0......",
  "e": "AQ......",
  "ext": true,
  "key_ops": [
    "sign"
  ],
  "kty": "RSA",
  "n": "vsbOUoFA......",
  "p": "5QJitCu9......",
  "q": "1ULfGui5......",
  "qi": "2cK4apee......"
}

public.key example:

{
  "alg": "RS256",
  "e": "AQ......",
  "ext": true,
  "key_ops": [
    "verify"
  ],
  "kty": "RSA",
  "n": "vsbOUoFA......"
}

# 2. Register public key and get kid

Select your channel from the channel settings on the LINE Developers Console and access the Basic settings tab. Next, click the Register public key button next to Assertion Signing Key and enter the public key generated in step 1. Click the Register button.

kid will be displayed if you succeed in registering your public key.

# Generate a JWT

You can use any JWT library (opens new window) or write your own code from scratch to generate a JWT from your Assertion Signing Key.

The JWT is a string made up of a header, payload, and signature; all are required fields.

Header

Property Description
alg Fixed property: "RS256"
typ Fixed property: "JWT"
kid Use the kid property obtained in Create an Assertion Signing Key.

This is an example of a decoded header value.

{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "536e453c-aa93-4449-8e90-add2608783c6"
}

Payload

Property Type Description
iss String Channel ID. Found on the LINE Developers Console. Must be equal to sub.
sub String Channel ID. Found on the LINE Developers Console. Must be equal to iss.
aud String https://api.line.me/
exp Number The expiration time of the JWT. Set this value in UNIX timestamp. The max lifetime of a JWT Assertion is 30 minutes.
token_exp Number Required when requesting a channel access token. This represents a valid expiration time for the channel access token in seconds. The max lifetime of a channel access token is 30 days.

This is an example of a decoded payload value.

{
  "iss": "1234567890",
  "sub": "1234567890",
  "aud": "https://api.line.me/",
  "exp": 1559702522,
  "token_exp": 86400
}

Signature

You can generate a JWT by signing the header and payload as shown above with your private key of your Assertion Signing Key. Here are examples of the steps for generating an Assertion Signing Key.

# Generate using Node.js library node-jose

To use the codes described here, you will need to install the following beforehand:

This is an example of the code used to generate a JWT signed with a private key using node-jose. To create your own JWT with this code, change the privateKey to the value of the private key of your Assertion Signing Key and change the values of header and payload, respectively, and run it. See node-jose (opens new window) for more information on how to use node-jose. Be sure to sign with your private key to prove that the content has not been tampered with.

Example code using node-jose

let jose = require('node-jose');

let privateKey = `
{
    "p": "4h8yEw4q9VkzhXMgXZsIZVkEuZ49EmtWYk9zs0hPTa24ejjRMA6KTYh_va0GlaChO9t0MVQVuduznt-OFZyRAinr4svU4MKD2A3gTHJJCxs0xICva8rkHXqxfPwXngpb5L_xFURbXcSTzMcKckWuOpyPznAgY4XsZxw0t7ewj9E",
    "kty": "RSA",
    "q": "pVhBdRN5K3MEiZzU4__TsrtSBJDD_stu60m73iIvsHIrvK3Dmfl-J1zhsyOvi3NH9mVXpUimBwP8nTe-BlVM71G7_EotFHeKH1zTmBlx6AOngmrc40W2Hd__OZW0NfC_xOTvI_Ea2BNGoGtcrIGVFLTivJ4y9wAVOKA058zJ0ls",
    "d": "ObzE_-TROJazDm-ry-8TKRBMGzwcwTK6lMFSk7n-Xp6h7cDauSdRRYnZivC1lh5plVG3I9aUmPTRbVk7nrPqOlp4WWKQ27lyLd5IogbArpXgnBSkp9Zy0lWzvOsI3gHNnYuehyksHB53FIK93t838JfDQoXUUzalNoNwAGfkTNZxT4GIXGMGzNck2Z_urOATMf8-wdad-u4a5IB2KfHugwH2kw-Zig7fbdcN4_DeKWpuigdesa48Yj_hRJRws-mVFp-xHlGJehumnM_v8FLD85ap8L1hwvBqdJQeurcLXYzZbtdp9a5GpJI7gzOTMoEdxIKlEIIbaOKv4rkkztdhoQ",
    "e": "AQAB",
    "use": "sig",
    "kid": "536e453c-aa93-4449-8e90-add2608783c6",
    "qi": "XQ2puK9LT5yimyJXlXb4nHEBzPGe3sYbaZW_gMK4iHuM8cseImwLNP8ZIeGaNx5X_hZ6ZOzkjtYJjY85fvaWa2UDGdGlEw3ZO-Nk0Qu_exBrqZgZAsua75TjpJRw01Yd1TNBx5MYuvhltJLsjW-uSjcE-rZoO74FEe9pYYeQjI4",
    "dp": "Qq_wlK4Y_ULRbwoFAZY3Y6xdOGDyofwF_fhwpu8sdDxHq8QV7ZZcM4GOKuJcjsRQyNZv7hxeS_H_h1tnC_igy4KRjtGOdrrnJ1DwVZte72eWqF1LXv73R7pnnfS7AmELuOriruL6Dy1qaXpKGmlyeNazkq5-3tsgXUh0Q7po2AE",
    "alg": "RS256",
    "dq": "Wj1ovDT8lLIZb-Ggby9YotuJT-SSk6UDzHZZikquLGajaD6N2qNILsOKivKXBEzOobN9uj-EHaAXZtbdZyd27cZ2CqORJvJ299b5xLFecXpNGeio1YFee7-c1BjYWfgjMZqgycT1GairizINSjkO3FY8ySSuPBBXhKgrN7eVDrE",
    "n": "kgwP0NPaoAwhSh9iLlRaT7FSRbNsl6T5-j-bB3xAT1UbsxOJ9v06S3_54bpYlEAkjlrO-i1vmSzfSVnqFXnjWThWRvPmBDth3Ka7hQm9UXjiAvTzYxXGFjyhALqa_-DQCtdrqIhi8E4hAuSu--kGgnFKg3G-21KJuqnVzsXrClGkxbmVufx0MJjJxr1YGfkTMG8i0dovS9tnkioDAkt1knupiYk5ir_WiNy4T-70T5s3ktC5_4Uz10hS-rWeUxiihzG8G7ceg84-Kt5jKP_AgUnel-ksRyfgSJCYC9nHyz913a3ALj3Dzt7TBaxwAjlxESrdNz5RE9DNDZfPmNWRSw"
  }
`;

let header = {
    alg: "RS256",
    typ: "JWT",
    kid: "536e453c-aa93-4449-8e90-add2608783c6"
};

let payload = {
    iss: "1234567890",
    sub: "1234567890",
    aud: "https://api.line.me/",
    exp: Math.floor(new Date().getTime() / 1000) + 60 * 30,
    token_exp: 60 * 60 * 24 * 30
};

jose.JWS.createSign({format: 'compact', fields: header}, JSON.parse(privateKey))
    .update(JSON.stringify(payload))
    .final()
    .then(result => {
        console.log(result);
    });

Sign the base64url-encoded header, base64url-encoded claim set, and a secret key (such as an rsa_private.pem file), using the algorithm you defined in the header. The signature is then base64url-encoded, and the result is the JWT.

Example encoded JWT

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjJjNjU4NWYzLThkZGQtNDZjNC05YmUyLWI1NGE3MGFhOTRlYSJ9.eyJpc3MiOiIxNjUzOTQ3MTcyIiwic3ViIjoiMTY1Mzk0NzE3MiIsImF1ZCI6Imh0dHBzOi8vYXBpLmxpbmUubWUvIiwiZXhwIjoiMTU4NTIwMDA2MiIsInRva2VuX2V4cCI6IjI1OTIwMDAifQ.UVG6PAEub-OPbZ3nJuVxRRPjY6Sz_eIHJV9DTTAHCR79YsG4yWvoa9AeIctibb6IJQKgTEV7mF7LsUDmXldEDqYwyEmKs38zj_995Ntc9SYBFphHpr09NqfMoqMphwKqms2NOnqgcHreFs27d9Q0Qv8Rtv2t7SB2cVO__KrsjzYNs3miTvDdkqYLXFo5fXwuzNtHOCAJomd6bhMR8Yd1-vJmtMCBPK4hmA98w8fG_NhcyLbw-B9AuxQ6z92zXiRhNyPlK_3ce2T7HtgUluJ4xJl4xdLJ_C6hvTAqtQxmSiJKzbjUiANF6hVBTomU8vkaIjEKjnlT1uPMihfrsA3pzQ

# Generate using Python library PyJWT

To use the codes described here, you will need to install the following beforehand:

This is an example of the code used to generate a JWT signed with a private key using PyJWT. To create your own JWT with this code, change the privateKey to the value of the private key of your Assertion Signing Key and change the values of kid and payload in header respectively, and run it. See PyJWT (opens new window) for more information on how to use PyJWT. Be sure to sign with your private key to prove that the content has not been tampered with.

Example of code using PyJWT

import jwt
from jwt.algorithms import RSAAlgorithm
import time

privateKey = {
  "alg": "RS256",
  "d": "dcA-LXLBRecBQbW7a8LKAriFJhnpXzwu2uNoVF_8-QmGVzI5682FWh_CWhl_B6J0fpmA-d7_EP0WCB3AGhxlyTP6ROoYJo7nygb_KMLREM7n64LFGbvNtw4jk7dmISXl_JuEX6CG09BBx4GLh9AGHSaK4v9B-dDvrNZlAo2mIjISHNcAPENbOl_XIOmZpJd56znjjc1gGKaYGbIm8unxHnPhL66IVYGRu8gxKfG6JUP7o370-VDfFOeaAR0HshTycP6M41jcDSjL6z9-J-Sh0zSZXqGS4u82TNtmwtRTzVwd0w30KQ0TTROTiNsz5apVHjpMvmAxRlbvcW41xIq8sQ",
  "dp": "PAWBMzwnwgc-yixarV30gemH6Wk15HfSUYpR4wJZUHemGx_LE5GXdnKoyy8G9DAl6XMpm7YVH8cPXgXYNh-JlAggvzUeH5A7KAV4ZPTNak4CI844GSbYIu_dPBcVAg0O6sxQWugYpPbPnMDpE7qf4KilSSVG3JKqEMxkYySjZZE",
  "dq": "LBA_q2YYnglCL41-1b3BmzCm-hs7Q-N__otDWO01I03VYnzU-vEQmxy6Fzrh2Y4Fgwp6D8iScu42AOyhE-T-qDNbAsCB0iZeFqm84g6VQAfDbknjIUZtcGvQgzy-zlrl253_QdyJvl2b44KT1hfoF0tDNA1rhOy7WlBM__rH0Pc",
  "e": "AQAB",
  "kty": "RSA",
  "n": "x2glWJ7baQV4vdElnAXA5yu8yFk4LpszkHW3Ey-BKGT3kGVLy3Jk3OvkwjBFOglXWeyTWe_rJkMYkBKuon5syZVjrjb24CmViAXGr6d6IvrYWj8IGZ6ElVABfnjGgZMVywmBb7hIh2p8QR0L8UJEuWjBU5nlwkMBpvnY2HXAVhvir8CN7WRj_GBMxxgg7wSuW1tV-7Qf44grMqJ0Je7zjflS4-TpI8Ox3nhamn0d7NIdQ3jNdTP7IZF61IvETgb_6NdFnfsN-aifJC-Ea3ZwhVcEGJ5z3MMoKSoChJmkJMiV9CldqGRnEDWwBugZHeEtn71eGVE3DAXAzrf525YHYQ",
  "p": "7eH8LAzNkITH6t7CWU5tPAmQlGQPkby66Yfq52tSZ43pQRz0CdtDYCQnGoBXvHzAHhzH4MjmNLOSGVimZK_dIRg5lJaPvVe6hgQ3pYud5WzPWsnQTsC7agQ2rfQglyFUtjwd1gWBIY4gwHj4BYG6Up3g0TlX1sf_juZxcLhkOsc",
  "q": "1pf-Pj2ZPL1nGqVcMVH_hfziIOBtjxc5vMGyHwTaLAA9y2xKfe_SRU8kUK2q5ZykJ8wMckR9Pduuyn-vp4q2FANVSN69G01pUKM2ppkgXuil2S3REmzniGdajZjkpWKaZ6z1tJ_xSv9ghx06Dbro8n___KnpBq6afb022anRxJc",
  "qi": "6L6SgH_pkyqq1Tb6QXPAGmtqVZT58Ljf3QTw6Tx5OdZ9NNvDReHHb64MgbUMLhLzGMeXGqDI5j0WLhtXv4ddCKWkF7OeKLUNuRP7yLpyYMazn8TEOjKHsgLAklenxcSgYaoO_wULh1mze1_ZO2PJNgvkIx_Xzr0XDUAqUp4W0jk",
  "use": "sig"
}

headers = {
    "alg": "RS256",
    "typ": "JWT",
    "kid": "9869e446-3489-4516-a83f-ec9214ad94d0"
}

payload = {
  "iss": "1234567890",
  "sub": "1234567890",
  "aud": "https://api.line.me/",
  "exp":int(time.time())+(60 * 30),
  "token_exp": 60 * 60 * 24 * 30
}

key = RSAAlgorithm.from_jwk(privateKey)

JWT = jwt.encode(payload, key, algorithm="RS256", headers=headers, json_encoder=None)
print(JWT)

Sign the base64url-encoded header, base64url-encoded claim set, and a secret key using the algorithm you defined in the header. The signature is then base64url-encoded, and the result is the JWT.

Example of encoded JWT

eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ijk4NjllNDQ2LTM0ODktNDUxNi1hODNmLWVjOTIxNGFkOTRkMCJ9.eyJpc3MiOiIxMjM0NTY3ODkwIiwic3ViIjoiMTIzNDU2Nzg5MCIsImF1ZCI6Imh0dHBzOi8vYXBpLmxpbmUubWUvIiwiZXhwIjoxNjIzOTk1NTk5LCJ0b2tlbl9leHAiOjI1OTIwMDB9.Zf32xTqgUHSYw2C2Mlmunqz_AtkaqvGh0msx9XJMX6QYLPT4m4QYF3PsER-zfbhbByNT4rH09JEMRP7bzcNMQ8l4n_WXwTyLkNciZYzF-sTiVHiZu4ucJm4_l8ni5NaqOVEntsCp1wQi8-VLjaMpQlQ7crCdouEMFFeyVwgERfH8ui6UZaJeIlJKRZTnO6iYvKYuLyUsqzowfwZo0hcnnZIXKnjZ81ukjH3_78EHXOD5ivovAT7CtmBoglm3Bvsi0N6PlEONLhHqpCleaYTXRmCykxDLP600JRvi5TYApaN-8n2Bo3FskXJLuxquWLP-LTfMDlkakmfEfcQCiz7daQ

# Issue channel access tokens v2.1

You can issue a channel access token v2.1 with the JWT assertion, generated by the procedure in Generate a JWT, specified.

To manage channel access tokens v2.1 using key IDs
  • The response when issuing a channel access token v2.1 includes a channel access token and a unique key ID (key_id) pair. To manage channel access tokens correctly, be sure to store the channel access token and key ID pair at the time of issuing.
  • The key ID is an identifier added to the Messaging API on June 22, 2020. If your app is using a channel access token v2.1 that doesn't have a key ID, we encourage you to re-issue a channel access token v2.1 and store the token and key ID pair. Change your app to always use the new token if the channel access token is re-issued.

Store the pair of issued access tokens and key ids

  1. To issue a channel access token, specify the generated JWT and execute the Issue channel access token v2.1 endpoint.
  2. Channel access token and key ID are returned from the LINE Platform.
  3. Store the channel access token and key ID pair in a database or other location.

# Revoke channel access token v2.1

You can revoke a channel access token v2.1 with a valid channel access token specified.

To identify valid channel access tokens

Even if specifying an invalid channel access token and executing the Revoke channel access token v2.1 endpoint, no error occurs. To get key IDs paired to the current valid channel access tokens, execute the Get all valid channel access token key IDs v2.1 endpoint. To identify the valid access token, match the obtained key ID to its respective channel access token.

Store the pair of issued access tokens and key ids

  1. Re-generate a JWT from the stored Assertion Signing Key.
  2. Execute the Get all valid channel access token key IDs v2.1 endpoint with the JWT specified.
  3. The valid channel access token and key ID are returned from the LINE Platform.
  4. Explore the database that stores the channel access token and key ID pair.
  5. Search for the channel access token and key ID pair that match the obtained key ID.
  6. Get the valid channel access token.
  7. Specify the valid channel access token and execute the Revoke channel access token v2.1 endpoint.
  8. The channel access token is revoked from the LINE Platform.