Après avoir terminé la configuration JWT dans Document360, votre application a besoin d’une route backend pour gérer la dernière étape du flux de connexion.
Voici l’itinéraire :
- Valide que l’utilisateur est déjà authentifié dans votre application
- Envoie une requête sécurisée à l’URL de génération de code de Document360
- Récupère un code d’autorisation à usage unique
- Redirige l’utilisateur vers le site de la base de connaissances avec ce code
Les lecteurs n’ont pas besoin d’un compte Document360 distinct. L’authentification est entièrement gérée via votre application. Un compte dans votre application suffit pour qu’un lecteur puisse accéder à la base de connaissances.
Exemples de code
Les exemples ci-dessous montrent comment implémenter la route d’authentification backend en C#, Node.js et 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)
};
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
};
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);
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());
}
}
}
Liste de contrôle des développeurs
Avant de lancer la mise en ligne, vérifiez ce qui suit :
- Enregistrez l’URL de connexion, l’URL de rappel et l’URL de génération de code dans vos paramètres JWT.
- Stockez le secret client de manière sécurisée. Il n’est exposé qu’une seule fois au moment de la création.
- Implémentez la logique backend pour appeler l’URL de génération de code en utilisant HTTP Basic Auth.
- Signez le JWT dans votre backend en utilisant votre secret client. Ne jamais exposer la logique de signes côté client.
- Imposez HTTPS à tous les points de terminaison impliqués dans le flux d’authentification.
- Tester le comportement de la session, l’expiration du token et le renouvellement automatique de la session avant la mise en ligne.
- Surveillez les journaux backend pour détecter les erreurs 401, qui indiquent généralement des codes d’autorisation expirés ou des incompatibilités de jetons.
Redirection vers une page spécifique après la connexion
Par défaut, après la connexion, les lecteurs sont redirigés vers la page d’accueil de votre base de connaissances.
- Si votre page d’accueil n’est pas publiée, les lecteurs sont redirigés vers la
/docspage. - Pour rediriger les lecteurs vers une autre page de votre base de connaissances, configurez la redirection en suivant le schéma d’URL suivant.
Motif URL
https://<Knowledge base URL>/jwt/authorize?code=<code>&redirectUrl=<redirect path>
Paramètres
<Knowledge base URL>: l’URL principale de votre site de base de connaissances.<code>: le code généré par le point de terminaison de génération de code Document360.<redirect path>: l’URL où vous souhaitez que les lecteurs atterrissent après la connexion.
Exemple
https://example.document360.io/jwt/authorize?code=FOTaS_SW6dLGytQXvrG_rRFGhyPvrDDrgxJAZzYvJcY&redirectUrl=/docs/5-basic-things-to-get-started
Document360 enverra l’URL de Redirection vers redirectPath le point de terminaison de connexion. Lorsque le point de terminaison de connexion redirige vers la base de connaissances avec le code d’authentification, il doit retourner l’URL de redirection comme redirectUrl paramètre.
Dans KB Site 2.0, la redirection est gérée via des cookies au lieu du redirectUrl paramètre. Si votre implémentation JWT est basée sur la redirection de chaînes de requête utilisant ce redirectUrl paramètre, l’approche basée sur les cookies ne prend pas en charge ce paramètre. Vous devrez peut-être mettre à jour votre implémentation ou contacter le support pour plus de précisions.