Renew JWT Token

This API renews an authentication token using a valid refresh token. Upon successful renewal:

  1. A new auth token and a new refresh token are issued.
  2. The old refresh token is invalidated and cannot be reused.
  3. Token validity is enforced by checking expiration (exp), revoked status, and not before (nbf) time.

If the refresh token is expired, already used, revoked, or attempted before the nbf time, the API returns an "invalid refresh token" error.

Endpoint

POST /app/{app_id}/renew

Headers

Name Type Required Description
Authorization string Yes The app-key of the project.

Example Request

curl --request POST \
--url http://{{your-endpoint}}:{{your-port}}/app/{{app-id}}/renew \
--header 'Authorization: {{app-key}}' \
--header 'content-type: application/json' \
--data '{
"refresh_token":"eyJhbGciOiJFUzI1NiIsImtpZCI6IjAxSk1DNlRDTTFDSDdOV0cwRThBNVNRVEhCIiwidHlwIjoiSldUIn0.eyJleHAiOjE3NDAwMDEzMDcsImlhdCI6MTczOTk5NzcwNywiaXNzIjoiYXBwX3VwZGF0ZTIiLCJqaWQiOiIwMUpNRlozWjI2WU42NkQ4SDhUMllUUVgxOSIsIm5iZiI6MTczOTk5Nzc2NywidHlwZSI6InJlZnJlc2gifQ.7GkonTcQytLzAKugOdzVA3qut9ZmPcik42Z6Ww9UckT6IB5WrrLKq_zSxXN0j2u8k0z_6ty4vhg7KtQOM0Ja3w",
"ip":"1.1.1.1",
"useragent":"test-agent"
}'
const request = require('request');

const options = {
  method: 'POST',
  url: 'http://{{your-endpoint}}:{{your-port}}/app/{{app-id}}/renew',
  headers: {Authorization: '{{app-key}}', 'content-type': 'application/json'},
  body: {
    refresh_token: 'eyJhbGciOiJFUzI1NiIsImtpZCI6IjAxSk1DNlRDTTFDSDdOV0cwRThBNVNRVEhCIiwidHlwIjoiSldUIn0.eyJleHAiOjE3NDAwMDEzMDcsImlhdCI6MTczOTk5NzcwNywiaXNzIjoiYXBwX3VwZGF0ZTIiLCJqaWQiOiIwMUpNRlozWjI2WU42NkQ4SDhUMllUUVgxOSIsIm5iZiI6MTczOTk5Nzc2NywidHlwZSI6InJlZnJlc2gifQ.7GkonTcQytLzAKugOdzVA3qut9ZmPcik42Z6Ww9UckT6IB5WrrLKq_zSxXN0j2u8k0z_6ty4vhg7KtQOM0Ja3w',
    ip: '1.1.1.1',
    useragent: 'test-agent'
  },
  json: true
};

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});
import requests

url = "http://{{your-endpoint}}:{{your-port}}/app/{{app-id}}/renew"

payload = {
    "refresh_token": "eyJhbGciOiJFUzI1NiIsImtpZCI6IjAxSk1DNlRDTTFDSDdOV0cwRThBNVNRVEhCIiwidHlwIjoiSldUIn0.eyJleHAiOjE3NDAwMDEzMDcsImlhdCI6MTczOTk5NzcwNywiaXNzIjoiYXBwX3VwZGF0ZTIiLCJqaWQiOiIwMUpNRlozWjI2WU42NkQ4SDhUMllUUVgxOSIsIm5iZiI6MTczOTk5Nzc2NywidHlwZSI6InJlZnJlc2gifQ.7GkonTcQytLzAKugOdzVA3qut9ZmPcik42Z6Ww9UckT6IB5WrrLKq_zSxXN0j2u8k0z_6ty4vhg7KtQOM0Ja3w",
    "ip": "1.1.1.1",
    "useragent": "test-agent"
}
headers = {
    "Authorization": "{{app-key}}",
    "content-type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
<?php
$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'http://{{your-endpoint}}:{{your-port}}/app/{{app-id}}/renew', [
  'body' => '{
  "refresh_token":"eyJhbGciOiJFUzI1NiIsImtpZCI6IjAxSk1DNlRDTTFDSDdOV0cwRThBNVNRVEhCIiwidHlwIjoiSldUIn0.eyJleHAiOjE3NDAwMDEzMDcsImlhdCI6MTczOTk5NzcwNywiaXNzIjoiYXBwX3VwZGF0ZTIiLCJqaWQiOiIwMUpNRlozWjI2WU42NkQ4SDhUMllUUVgxOSIsIm5iZiI6MTczOTk5Nzc2NywidHlwZSI6InJlZnJlc2gifQ.7GkonTcQytLzAKugOdzVA3qut9ZmPcik42Z6Ww9UckT6IB5WrrLKq_zSxXN0j2u8k0z_6ty4vhg7KtQOM0Ja3w",
  "ip":"1.1.1.1",
  "useragent":"test-agent"
}',
  'headers' => [
    'Authorization' => '{{app-key}}',
    'content-type' => 'application/json',
  ],
]);

echo $response->getBody();
package main

import (
  "fmt"
  "strings"
  "net/http"
  "io"
)

func main() {

  url := "http://{{your-endpoint}}:{{your-port}}/app/{{app-id}}/renew"

  payload := strings.NewReader("{\n  \"refresh_token\":\"eyJhbGciOiJFUzI1NiIsImtpZCI6IjAxSk1DNlRDTTFDSDdOV0cwRThBNVNRVEhCIiwidHlwIjoiSldUIn0.eyJleHAiOjE3NDAwMDEzMDcsImlhdCI6MTczOTk5NzcwNywiaXNzIjoiYXBwX3VwZGF0ZTIiLCJqaWQiOiIwMUpNRlozWjI2WU42NkQ4SDhUMllUUVgxOSIsIm5iZiI6MTczOTk5Nzc2NywidHlwZSI6InJlZnJlc2gifQ.7GkonTcQytLzAKugOdzVA3qut9ZmPcik42Z6Ww9UckT6IB5WrrLKq_zSxXN0j2u8k0z_6ty4vhg7KtQOM0Ja3w\",\n  \"ip\":\"1.1.1.1\",\n  \"useragent\":\"test-agent\"\n}")

  req, _ := http.NewRequest("POST", url, payload)

  req.Header.Add("Authorization", "{{app-key}}")
  req.Header.Add("content-type", "application/json")

  res, _ := http.DefaultClient.Do(req)

  defer res.Body.Close()
  body, _ := io.ReadAll(res.Body)

  fmt.Println(res)
  fmt.Println(string(body))

}
AsyncHttpClient client = new DefaultAsyncHttpClient();
client.prepare("POST", "http://{{your-endpoint}}:{{your-port}}/app/{{app-id}}/renew")
  .setHeader("Authorization", "{{app-key}}")
  .setHeader("content-type", "application/json")
  .setBody("{\n  \"refresh_token\":\"eyJhbGciOiJFUzI1NiIsImtpZCI6IjAxSk1DNlRDTTFDSDdOV0cwRThBNVNRVEhCIiwidHlwIjoiSldUIn0.eyJleHAiOjE3NDAwMDEzMDcsImlhdCI6MTczOTk5NzcwNywiaXNzIjoiYXBwX3VwZGF0ZTIiLCJqaWQiOiIwMUpNRlozWjI2WU42NkQ4SDhUMllUUVgxOSIsIm5iZiI6MTczOTk5Nzc2NywidHlwZSI6InJlZnJlc2gifQ.7GkonTcQytLzAKugOdzVA3qut9ZmPcik42Z6Ww9UckT6IB5WrrLKq_zSxXN0j2u8k0z_6ty4vhg7KtQOM0Ja3w\",\n  \"ip\":\"1.1.1.1\",\n  \"useragent\":\"test-agent\"\n}")
  .execute()
  .toCompletableFuture()
  .thenAccept(System.out::println)
  .join();

client.close();

Request Fields

Field Type Required Description
refresh_token string Yes The refresh token.
ip string Yes The IP address of the user.
useragent string Yes The user agent string of the request.

Example Success Response

{
"auth_token": "eyJhbGciOiJFUzI1NiIsImtpZCI6IjAxSk1WMjhGSlZCS0YwSkcwWVNHNjU1RUhZIiwidHlwIjoiSldUIn0.eyJhdWQiOiJ3ZWItYXBwIiwiZXhwIjoxNzQwNDMwNjQ5LCJpYXQiOjE3NDA0MjcwNDksImlwIjoiMS4xLjEuMSIsImlzcyI6ImFwcF8yIiwiamlkIjoiMDFKTVdSSkVKNzExOTFLUDg4TkRHV1ZZRVciLCJuYmYiOjE3NDA0MjcwNDksInBlcnNvbmFsIjp7Im5hbWUiOiJ0ZXN0LXVzZXIifSwic3ViIjoidGVzdEB0ZXN0LmNvbSIsInVzZXJhZ2VudCI6Im15LXVzZXItYWdlbnQifQ.fYudl5pllihNZ01QTsws1O6yQ6EWlG1_9YxnZ4TRuNvV4mIZumcjFowMFb4NoZKYFV2UogCIbWpnm6aI9US__A",
"key_id": "01JMV28FJVBKF0JG0YSG655EHY",
"public_key": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFOWpkd1JWSTFTT2RsNGJDNkd5MHRycUVNc01DNQo2NW9VK3ltV2MvQ09JUTJlM0tkNGwvNkoweUlUMTFnT25UdERhOGdudXZKZ1JKc2JXNWQxZkMzUk5BPT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==",
"refresh_token": "eyJhbGciOiJFUzI1NiIsImtpZCI6IjAxSk1WMjhGSlZCS0YwSkcwWVNHNjU1RUhZIiwidHlwIjoiSldUIn0.eyJleHAiOjE3NDA0Mzc4NDksImlhdCI6MTc0MDQyNzA0OSwiaXNzIjoiYXBwXzIiLCJqaWQiOiIwMUpNV1JKRUo3MTE5MUtQODhOREdXVllFVyIsIm5iZiI6MTc0MDQyNzEwOSwidHlwZSI6InJlZnJlc2gifQ.Vs6SCJ--KVAurGTliv7yfEjEzZVFgWGkcSviQWHNG_hKZHNuWamCEYJk-dLwugz7unAUG0Lyas1wgLTtgLrvCQ"
}

Response Fields

Field Type Description
auth_token string Signed JWT authentication token.
key_id string (ULID) A unique identifier of the public-private key pair was used for the signing.
public_key string Base64-encoded public key, can be use for frontend JWT verification.
refresh_token string Signed JWT refresh token for session renewal.

Example Error Response

{
  "message": "invalid refresh token"
}

Possible Reasons for Failure:

  1. Expired Refresh Token – The refresh token is no longer valid.
  2. Already Used Refresh Token – Refresh tokens can only be used once.
  3. Revoked Key – The signing key has been invalidated.
  4. Used Before nbf Time – The refresh token is not yet valid.

Security Considerations:

  1. Refresh tokens can only be used once—using an old refresh token will fail.
  2. New tokens are signed with an updated key (in case of auto rotation of the signing keys), ensuring continuous security.
  3. Token expiration and nbf time ensure that outdated or premature tokens are rejected.

Responses

Status Code Description
200 Ok Success
400 Bad Request Mostly when the form validation fails. The error will be returned as a response.
403 Access Denied When the provided app key in Authorization header is invalid.
500 Internal Server Error Mostly because of the database error. Check the log for root cause details.