JavaScript · API · Intermediate

JWT Authentication

5 tasks

Use JSON Web Tokens for authentication, understand access/refresh token flow, and handle token refresh.

JWT — access and refresh tokens

#
JWT (JSON Web Token) authentication uses two tokens instead of one: - **Access token** — short-lived (minutes/hours). Send this in every request as `Bearer <access_token>`. - **Refresh token** — long-lived (days/weeks). Use it to get a new access token when the old one expires. **Get both tokens:** ```javascript const response = await fetch('https://apilearn.tukas.dev/api/auth/jwt/', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: 'alice42', password: 'SecurePass123!' }), }); const { access, refresh } = await response.json(); ``` **Use the access token — note `Bearer`, not `Token`:** ```javascript const profile = await fetch('https://apilearn.tukas.dev/api/users/profile/', { headers: { 'Authorization': `Bearer ${access}` }, }).then(r => r.json()); ``` **Refresh — get a new access token:** ```javascript const response = await fetch('https://apilearn.tukas.dev/api/auth/jwt/refresh/', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ refresh }), }); const { access: newAccess, refresh: newRefresh } = await response.json(); ``` The refresh endpoint returns both a new access token and a new refresh token — update both.

Getting and refreshing JWT tokens

#
Full JWT flow with refresh: ```javascript const BASE_URL = 'https://apilearn.tukas.dev'; async function getJwtTokens(username, password) { const response = await fetch(`${BASE_URL}/api/auth/jwt/`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }), }); return response.json(); // { access, refresh } } async function refreshTokens(refreshToken) { const response = await fetch(`${BASE_URL}/api/auth/jwt/refresh/`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ refresh: refreshToken }), }); return response.json(); // { access, refresh } } async function main() { // Step 1: get initial tokens let { access, refresh } = await getJwtTokens('YOUR_USERNAME', 'YOUR_PASSWORD'); console.log('Access token:', access.slice(0, 20) + '...'); // Step 2: use access token const profile = await fetch(`${BASE_URL}/api/users/profile/`, { headers: { 'Authorization': `Bearer ${access}` }, }).then(r => r.json()); console.log('Profile:', profile.username); // Step 3: simulate refresh (in practice, do this when you get 401) const refreshed = await refreshTokens(refresh); access = refreshed.access; refresh = refreshed.refresh; console.log('New access token:', access.slice(0, 20) + '...'); // Step 4: use new access token const cart = await fetch(`${BASE_URL}/api/cart/`, { headers: { 'Authorization': `Bearer ${access}` }, }).then(r => r.json()); console.log('Cart items:', cart.items.length); } main(); ```

JWT reference

#
**Endpoints** ``` POST /api/auth/jwt/ Get access + refresh tokens body: { username, password } returns: { access, refresh } POST /api/auth/jwt/refresh/ Get new tokens using refresh token body: { refresh } returns: { access, refresh } ``` **Using the access token** ```javascript headers: { 'Authorization': `Bearer ${access}` } // Note: "Bearer" not "Token" — different from token auth ``` **Token vs JWT comparison** ``` Token auth One token, never expires "Token <token>" JWT auth access + refresh pair "Bearer <access>" access expires, refresh rotates ``` **Refresh pattern** ```javascript let { access, refresh } = await getJwtTokens(user, pass); // When access token expires (401): const tokens = await refreshTokens(refresh); access = tokens.access; refresh = tokens.refresh; // also update refresh ``` **JWT structure (informational)** ``` header.payload.signature — three base64 parts separated by dots atob(access.split('.')[1]) — decode payload (contains exp, user_id, etc.) ```
01

Get JWT tokens

#

POST to `/api/auth/jwt/` with your credentials. Print both the `access` and `refresh` tokens (first 30 chars of each is enough).

const BASE_URL = 'https://apilearn.tukas.dev';

async function main() {
  // POST /api/auth/jwt/ and print access + refresh (first 30 chars)
}

main();
Solution
const BASE_URL = 'https://apilearn.tukas.dev';

async function main() {
  const response = await fetch(`${BASE_URL}/api/auth/jwt/`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ username: 'YOUR_USERNAME', password: 'YOUR_PASSWORD' }),
  });
  const { access, refresh } = await response.json();
  console.log('access:', access.slice(0, 30) + '...');
  console.log('refresh:', refresh.slice(0, 30) + '...');
}

main();
02

Use access token for a request

#

Get JWT tokens, then use the `access` token with `Bearer` prefix to fetch `/api/users/profile/`. Print `username` and `email`.

const BASE_URL = 'https://apilearn.tukas.dev';

async function main() {
  // get JWT, use access token with Bearer prefix to fetch profile
}

main();
Solution
const BASE_URL = 'https://apilearn.tukas.dev';

async function main() {
  const { access } = await fetch(`${BASE_URL}/api/auth/jwt/`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ username: 'YOUR_USERNAME', password: 'YOUR_PASSWORD' }),
  }).then(r => r.json());

  const profile = await fetch(`${BASE_URL}/api/users/profile/`, {
    headers: { 'Authorization': `Bearer ${access}` },
  }).then(r => r.json());

  console.log(profile.username);
  console.log(profile.email);
}

main();
03

Refresh the access token

#

Get initial JWT tokens, then immediately call `/api/auth/jwt/refresh/` with the `refresh` token. Print the new `access` token (first 30 chars) and confirm it's different from the old one.

const BASE_URL = 'https://apilearn.tukas.dev';

async function main() {
  // get tokens, then refresh, compare old and new access
}

main();
Solution
const BASE_URL = 'https://apilearn.tukas.dev';

async function main() {
  const { access: oldAccess, refresh } = await fetch(`${BASE_URL}/api/auth/jwt/`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ username: 'YOUR_USERNAME', password: 'YOUR_PASSWORD' }),
  }).then(r => r.json());

  const { access: newAccess } = await fetch(`${BASE_URL}/api/auth/jwt/refresh/`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ refresh }),
  }).then(r => r.json());

  console.log('Old:', oldAccess.slice(0, 30));
  console.log('New:', newAccess.slice(0, 30));
  console.log('Different:', oldAccess !== newAccess);
}

main();
04

Decode the JWT payload

#

Get an access token. The JWT payload is the middle part (between the two dots), base64-encoded. Decode it with `atob()` and `JSON.parse()`. Print `user_id` and `exp` from the payload.

const BASE_URL = 'https://apilearn.tukas.dev';

async function main() {
  // get access token
  // split by '.', take index 1, decode with atob, parse JSON
  // print user_id and exp
}

main();
Solution
const BASE_URL = 'https://apilearn.tukas.dev';

async function main() {
  const { access } = await fetch(`${BASE_URL}/api/auth/jwt/`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ username: 'YOUR_USERNAME', password: 'YOUR_PASSWORD' }),
  }).then(r => r.json());

  const payload = JSON.parse(atob(access.split('.')[1]));
  console.log('user_id:', payload.user_id);
  console.log('exp:', payload.exp);
  console.log('expires:', new Date(payload.exp * 1000).toISOString());
}

main();
05

Full JWT flow

#

Write a complete script: get tokens, fetch the profile with `access`, refresh the tokens, then fetch the cart with the new `access`. Print username from the profile and item count from the cart.

const BASE_URL = 'https://apilearn.tukas.dev';

async function main() {
  // 1. get JWT tokens
  // 2. fetch profile with access
  // 3. refresh tokens
  // 4. fetch cart with new access
}

main();
Solution
const BASE_URL = 'https://apilearn.tukas.dev';

async function main() {
  // 1. Initial tokens
  let { access, refresh } = await fetch(`${BASE_URL}/api/auth/jwt/`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ username: 'YOUR_USERNAME', password: 'YOUR_PASSWORD' }),
  }).then(r => r.json());

  // 2. Fetch profile
  const profile = await fetch(`${BASE_URL}/api/users/profile/`, {
    headers: { 'Authorization': `Bearer ${access}` },
  }).then(r => r.json());
  console.log('Username:', profile.username);

  // 3. Refresh
  ({ access, refresh } = await fetch(`${BASE_URL}/api/auth/jwt/refresh/`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ refresh }),
  }).then(r => r.json()));

  // 4. Fetch cart with new token
  const cart = await fetch(`${BASE_URL}/api/cart/`, {
    headers: { 'Authorization': `Bearer ${access}` },
  }).then(r => r.json());
  console.log('Cart items:', cart.items.length);
}

main();