--- title: "Implement JWT in your application" slug: "implement-jwt-in-your-application" description: "Implement backend authentication for Document360 with C#, Node.js, or Java. Ensure secure login flow and proper redirection to your knowledge base." updated: 2026-09-28T09:16:30Z published: 2026-09-28T09:16:30Z canonical: "docs.document360.com/implement-jwt-in-your-application" --- > ## Documentation Index > Fetch the complete documentation index at: https://docs.document360.com/llms.txt > Use this file to discover all available pages before exploring further. # Implement JWT in your application After completing the JWT configuration in Document360, your application needs a backend route to handle the final step of the login flow. This route: - Validates that the user is already authenticated in your application - Sends a secure request to Document360's Code generation URL - Retrieves a one-time authorization code - Redirects the user to the knowledge base site with that code NOTE Readers do not require a separate Document360 account. Authentication is handled entirely through your application. An account in your application is sufficient for a reader to access the knowledge base. --- ## Code samples The examples below show how to implement the backend authentication route in C#, Node.js, and Java. ### C# ``` /// /// Example endpoint to authenticate a user and retrieve a token from the identity server, /// and redirect the user to the Knowledge Base (KB) using the token code. /// /// Client ID issued for your application /// Client secret associated with the client ID /// Redirects to the KB with the issued code [HttpGet] [Route("authenticate")] public async Task AuthenticateAsync(string clientId, string clientSecret) { if (!HttpContext.User.Identity.IsAuthenticated) { // user is not authenticated, redirect to an error or login page return Unauthorized(new { message = "User not authenticated" }); } // Ensure you have the correct client ID and secret from your Document360 JWT configuration var authToken = Encoding.ASCII.GetBytes($"{clientId}:{clientSecret}"); // Create an HttpClient instance using var httpClient = new HttpClient(); // Set the Authorization header with Basic authentication httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(authToken)); // Prepare the payload with user information var payload = new { username = User.Identity.Name, firstName = "FirstName", // Replace or customize as needed lastName = "LastName", emailId = "user@example.com", // Replace with actual user email readerGroupIds = new List { "group1", "group2" }, // Replace with actual reader group IDs if needed (Optional) tokenValidity = 3600, // Token validity in seconds (Optional, default is 5 minutes) providerName = "app.example.com" // Domain of the matching JWT configuration (Required when multiple JWT configurations are active) }; var payloadContent = new StringContent(JsonConvert.SerializeObject(payload), Encoding.UTF8, "application/json"); // Identity server token endpoint - replace with your actual URL string identityServerUrl = "codeGeneration endpoint, you can find that in JWT config portal"; // KB login URL to redirect after successful token issuance string kbLoginUrl = "https://{your subdomain}.document360.io"; var response = await httpClient.PostAsync(identityServerUrl, payloadContent); if (response.IsSuccessStatusCode) { var content = await response.Content.ReadAsStringAsync(); var tokenJson = JObject.Parse(content); // Extract the code from the response var tokenCode = (string)tokenJson.SelectToken("code"); // Construct the KB login URL with code query parameter string finalRedirectUrl = $"{kbLoginUrl}?code={tokenCode}"; return Redirect(finalRedirectUrl); } else { // Handle error response from the identity server var error = await response.Content.ReadAsStringAsync(); return StatusCode((int)response.StatusCode, new { error = "Token request failed", details = error }); } } ``` ### Node.js ``` const express = require('express'); const https = require('https'); const axios = require('axios'); const app = express(); app.use(express.json()); const clientId = 'your-client-id'; const clientSecret = 'your-client-secret'; const codeGenerationUrl = 'https://identity.document360.net/jwt/generateCode'; const kbLoginUrl = 'https://your-subdomain.document360.net/jwt/authorize'; app.get('/authenticate', async (req, res) => { try { const isAuthenticated = true; // Replace with your actual auth logic if (!isAuthenticated) { return res.status(401).json({ message: 'User not authenticated' }); } const authHeader = Buffer.from(`${clientId}:${clientSecret}`).toString('base64'); const payload = { username: 'john.doe', firstName: 'John', lastName: 'Doe', emailId: 'john.doe@example.com', readerGroupIds: ['group1', 'group2'], tokenValidity: 3600, providerName: 'app.example.com' // Domain of the matching JWT configuration }; const response = await axios.post( codeGenerationUrl, payload, { headers: { 'Authorization': `Basic ${authHeader}`, 'Content-Type': 'application/json', 'Accept': 'application/json' }, httpsAgent: new https.Agent({ maxVersion: 'TLSv1.2' }) } ); const tokenCode = response.data?.code; if (!tokenCode) { return res.status(500).json({ message: 'No code received from Document360' }); } console.log("Redirecting to:", `${kbLoginUrl}?code=${tokenCode}`); return res.redirect(`${kbLoginUrl}?code=${tokenCode}`); } catch (error) { return res.status(500).json({ message: 'JWT SSO failed', details: error.response?.data || error.message }); } }); ``` ### Java ``` import org.springframework.http.*; import org.springframework.web.bind.annotation.*; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; import java.nio.charset.StandardCharsets; import java.util.*; @RestController public class JwtSsoController { private final String clientId = "your-client-id"; private final String clientSecret = "your-client-secret"; private final String codeGenerationUrl = "https://identity.document360.io/api/jwt/generate-code"; private final String kbLoginUrl = "https://your-subdomain.document360.io/jwt/authorize"; @GetMapping("/authenticate") public ResponseEntity authenticate() { // Example: Check if user is authenticated in your system boolean isAuthenticated = true; // Replace with actual logic if (!isAuthenticated) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("User not authenticated"); } // Create Basic Auth header String auth = clientId + ":" + clientSecret; String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes(StandardCharsets.UTF_8)); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); headers.set("Authorization", "Basic " + encodedAuth); headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); // Construct payload Map payload = new HashMap<>(); payload.put("username", "john.doe"); payload.put("firstName", "John"); payload.put("lastName", "Doe"); payload.put("emailId", "john.doe@example.com"); payload.put("readerGroupIds", Arrays.asList("group1", "group2")); payload.put("tokenValidity", 3600); payload.put("providerName", "app.example.com"); // Domain of the matching JWT configuration HttpEntity> request = new HttpEntity<>(payload, headers); RestTemplate restTemplate = new RestTemplate(); try { ResponseEntity response = restTemplate.postForEntity(codeGenerationUrl, request, Map.class); if (response.getStatusCode() == HttpStatus.OK && response.getBody() != null) { String tokenCode = (String) response.getBody().get("code"); if (tokenCode == null || tokenCode.isEmpty()) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body("No code returned from Document360"); } // Redirect to the KB site with code String redirectUrl = UriComponentsBuilder.fromHttpUrl(kbLoginUrl) .queryParam("code", tokenCode) .toUriString(); HttpHeaders redirectHeaders = new HttpHeaders(); redirectHeaders.setLocation(java.net.URI.create(redirectUrl)); return new ResponseEntity<>(redirectHeaders, HttpStatus.FOUND); } else { return ResponseEntity.status(HttpStatus.BAD_GATEWAY) .body("Failed to get code from Document360: " + response.getStatusCode()); } } catch (Exception ex) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body("JWT SSO error: " + ex.getMessage()); } } } ``` --- ## Re-authenticating readers with multiple JWT configurations When a reader's session expires, Document360 sends them to sign in again. The redirect depends on how many JWT configurations are active in your project: - **One JWT configuration**: Document360 redirects the reader directly to the configured Login URL. - **Multiple JWT configurations**: Document360 cannot tell which configuration the reader signed in through, so the reader lands on a sign-in page and must enter their domain. To send readers straight back to the correct Login URL, add `providerName` to the payload your backend sends to the Code generation URL. Set its value to the **Domain** of the matching JWT configuration, available in **Settings** > **Users & Permissions** > **JWT**. ``` { "username": "john.doe", "firstName": "John", "lastName": "Doe", "emailId": "john.doe@example.com", "readerGroupIds": ["group1", "group2"], "tokenValidity": 3600, "providerName": "app.example.com" } ``` **How it works** 1. Your application sends `providerName` when the reader signs in. 2. Document360 records the JWT configuration the reader signed in through. 3. When the session expires, Document360 redirects the reader directly to that configuration's Login URL, without showing the domain entry page. NOTE If your project has only one JWT configuration, `providerName` is optional. Document360 already redirects readers to the configured Login URL on session expiry. --- ## Developer checklist Before going live, verify the following: - Register the Login URL, Callback URL, and Code generation URL in your JWT settings. - Store the Client Secret securely. It is displayed only once at the time of creation. - Implement backend logic to call the Code generation URL using HTTP Basic Auth. - Sign the JWT in your backend using your client secret. Never expose signing logic on the client side. - If your project has multiple JWT configurations, include `providerName` in the payload with the Domain of the matching configuration. - Enforce HTTPS on all endpoints involved in the authentication flow. - Test session behavior, token expiry, and automatic session renewal before going live. - Monitor backend logs for 401 errors, which typically indicate expired authorization codes or token mismatches. --- ## Redirecting to a specific page after login By default, after logging in, readers are redirected to the **Home page** of your knowledge base. - If your Home page is unpublished, readers are redirected to the `/docs` page. - To redirect readers to a different page in your knowledge base, configure the redirection using the following URL pattern. **URL pattern** ``` https:///jwt/authorize?code=&redirectUrl= ``` **Parameters** - `<Knowledge base URL>`: the main URL of your knowledge base site. - `<code>`: the code generated by the Document360 code generation endpoint. - `<redirect path>`: the URL where you want readers to land after login. **Example** ``` https://example.document360.io/jwt/authorize?code=FOTaS_SW6dLGytQXvrG_rRFGhyPvrDDrgxJAZzYvJcY&redirectUrl=/docs/5-basic-things-to-get-started ``` NOTE Document360 will send the Redirection URL as `redirectPath` to the login endpoint. When the login endpoint redirects back to the knowledge base with the authentication code, it should return the Redirection URL as a `redirectUrl` parameter. In **KB Site 2.0**, redirection is handled using cookies instead of the `redirectUrl` parameter. If your JWT implementation is based on query string redirection using the `redirectUrl` parameter, the cookie-based approach does not support this parameter. You may need to update your implementation or [contact support](mailto:support@document360.com) for further clarification. ## Related - [Configure JWT for the knowledge base widget](/configuring-jwt-for-the-knowledge-base-widget.md) - [Test your JWT configuration](/test-your-jwt-configuration.md)