Tras completar la configuración de JWT en Document360, tu aplicación necesita una ruta backend para gestionar el paso final del flujo de inicio de sesión.
Esta ruta:
- Valida que el usuario ya está autenticado en tu aplicación
- Envía una solicitud segura a la URL de generación de código de Document360
- Recupera un código de autorización de un solo uso
- Redirige al usuario al sitio de la base de conocimientos con ese código
Los lectores no requieren una cuenta separada de Document360. La autenticación se gestiona íntegramente a través de tu aplicación. Una cuenta en tu aplicación es suficiente para que un lector acceda a la base de conocimientos.
Ejemplos de código
Los ejemplos siguientes muestran cómo implementar la ruta de autenticación backend en C#, Node.js y Java.
C #
/// <summary>
/// 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.
/// </summary>
/// <param name="clientId">Client ID issued for your application</param>
/// <param name="clientSecret">Client secret associated with the client ID</param>
/// <returns>Redirects to the KB with the issued code</returns>
[HttpGet]
[Route("authenticate")]
public async Task<IActionResult> 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<string> { "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<String, Object> 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<Map<String, Object>> request = new HttpEntity<>(payload, headers);
RestTemplate restTemplate = new RestTemplate();
try {
ResponseEntity<Map> 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-autenticación de lectores con múltiples configuraciones JWT
Cuando termina la sesión de un lector, Document360 les envía para iniciar sesión de nuevo. La redirección depende de cuántas configuraciones de JWT estén activas en tu proyecto:
- Una configuración JWT: Document360 redirige al lector directamente a la URL de inicio de sesión configurada.
- Múltiples configuraciones JWT: Document360 no puede saber a través de qué configuración inició sesión el lector, por lo que el lector aterriza en una página de inicio de sesión y debe introducir su dominio.
Para enviar a los lectores directamente a la URL correcta de inicio de sesión, añade providerName a la carga útil que tu backend envía a la URL de generación de código. Establece su valor en el dominio de la configuración JWT correspondiente, disponible en Configuración > Usuarios y Permisos > JWT.
{
"username": "john.doe",
"firstName": "John",
"lastName": "Doe",
"emailId": "john.doe@example.com",
"readerGroupIds": ["group1", "group2"],
"tokenValidity": 3600,
"providerName": "app.example.com"
}
Cómo funciona
- Tu solicitud se envía
providerNamecuando el lector inicia sesión. - Document360 registra la configuración JWT a través de la cual el lector inició sesión.
- Cuando la sesión expira, Document360 redirige al lector directamente a la URL de inicio de sesión de esa configuración, sin mostrar la página de entrada del dominio.
Si tu proyecto solo tiene una configuración JWT, providerName es opcional. Document360 ya redirige a los lectores a la URL de inicio de sesión configurada al expirar la sesión.
Lista de comprobación para desarrolladores
Antes de lanzarse, verifica lo siguiente:
- Registra la URL de Inicio de Sesión, la URL de Llamada y la URL de generación de código en la configuración de tu JWT.
- Guarda el Secreto del Cliente de forma segura. Solo se muestra una vez en el momento de la creación.
- Implementa lógica backend para llamar a la URL de generación de código usando HTTP Basic Auth.
- Firma el JWT en tu backend usando tu secreto de cliente. Nunca expongas la lógica de firmas en el lado del cliente.
- Si tu proyecto tiene varias configuraciones JWT, inclúyelo
providerNameen la carga útil con el dominio de la configuración correspondiente. - Aplica HTTPS en todos los endpoints implicados en el flujo de autenticación.
- Prueba el comportamiento de la sesión, la expiración del token y la renovación automática de la sesión antes de lanzarse.
- Monitoriza los registros del backend en busca de errores 401, que normalmente indican códigos de autorización caducados o discrepancias de tokens.
Redirigir a una página específica después de iniciar sesión
Por defecto, tras iniciar sesión, los lectores son redirigidos a la página principal de tu base de conocimiento.
- Si tu página principal no está publicada, los lectores son redirigidos a la
/docspágina. - Para redirigir a los lectores a una página diferente de tu base de conocimiento, configura la redirección usando el siguiente patrón de URL.
Patrón de URL
https://<Knowledge base URL>/jwt/authorize?code=<code>&redirectUrl=<redirect path>
Parámetros
<Knowledge base URL>: la URL principal de tu sitio de base de conocimiento.<code>: el código generado por el endpoint de generación de código Document360.<redirect path>: la URL a la que quieres que los lectores lleguen tras iniciar sesión.
Ejemplo
https://example.document360.io/jwt/authorize?code=FOTaS_SW6dLGytQXvrG_rRFGhyPvrDDrgxJAZzYvJcY&redirectUrl=/docs/5-basic-things-to-get-started
Document360 enviará la URL de Redirección al redirectPath punto final de inicio de sesión. Cuando el punto final de inicio de sesión redirige de nuevo a la base de conocimiento con el código de autenticación, debería devolver la URL de Redirección como redirectUrl parámetro.
En KB Site 2.0, la redirección se gestiona usando cookies en lugar del redirectUrl parámetro. Si tu implementación en JWT se basa en la redirección de cadenas de consulta usando el redirectUrl parámetro, el enfoque basado en cookies no soporta este parámetro. Puede que necesites actualizar tu implementación o contactar con soporte para más aclaraciones.