Autentica con Immersive tramite codice: password, MFA e chiavi API

Documentation

Questa guida spiega come un'applicazione chiama direttamente le interfacce di autenticazione RestFrontage, ottenendo un Token immersivo e lo utilizza con WebAPI e OData. Gli esempi non richiedono DLL Immersive.

Varianti HTTP, C#, JavaScript, TypeScript, Python e PowerShell coprono applicazioni .NET, interfacce web, backend e script di exploit. Gli esempi HTTP mostrano il contratto; altre varianti eseguono gli stessi scambi.

In questo articolo

    Mostra altro
    Mostra meno

    1. Scegli la modalità giusta

    Bisogno Moda Risultato
    Effettua l'accesso a un utente quando la sua password è sufficiente secondo la policy del server. POST /api/Security/Users/Authenticate Un oggetto che contiene Gettone, oppure un rifiuto se è richiesto un MFA.
    Accedi a un utente con supporto MFA e la sua prima configurazione. POST /api/Security/Mfa/VerifyCredentials, poi VerifyTotp se richiesto. Così sia Gettone Immediatamente, un passo CeremoniToken da completare prima di ottenere il Token.
    Chiama un'API da un servizio o da un'automazione. Titoli x-app-name e x-api-key. La chiamata viene autenticata direttamente; la risposta rimane quella dell'API chiamata.
    Avere RestFrontage display login e MFA, senza creare queste schermate. Browser/Autorizzazione e Browser/Token, con PKCE. Un token dopo il viaggio nel browser; questa modalità è l'argomento dell'articolo dedicato all'interfaccia di connessione ospitata.

    Una chiave API rappresenta l'applicazione con I diritti del conto ad essa associato. Può essere utilizzato senza password o TOTP. Non concede automaticamente diritti di amministratore.

    Per un'app utilizzata da una persona, il viaggio ospitato evita di implementare il modulo password, il codice QR e l'input MFA. Questa guida copre le chiamate dirette quando la tua app supporta queste interazioni da sola.

    flowchart TD
        A{"Type de connexion ?"}
    
        A -->|Clé API| B["Envoyer x-app-name et x-api-key à chaque appel"]
        B --> C["Appeler les WebAPI ou OData"]
    
        A -->|Compte utilisateur| D["VerifyCredentials : vérifier l'identifiant et le mot de passe"]
        D --> E{"Réponse du serveur"}
    
        E -->|authenticated| F["Token disponible"]
        E -->|totp_required| G["Saisir le code du téléphone"]
        E -->|totp_enrollment_required| H["Configurer l'application TOTP avec le QR code"]
    
        H --> G
        G --> I["VerifyTotp : vérifier le code"]
        I -->|Code accepté| F
        I -->|Code incorrect, nouvel essai autorisé| G
    
        F --> J["Envoyer Authorization: Bearer TOKEN à chaque appel"]
        J --> C
    

    2. Preparazione alle chiamate

    2.1 Indirizzo, HTTPS e identità

    Fai affidamento, ad esempio, all'origine HTTPS di RestFrontage https://localhost:44333 oppure https://rest.exemple.fr sulla tua rete. Il certificato deve essere riconosciuto dal cliente.

    La password e la chiave API provengono dall'input dell'utente o dall'archivio segreto della tua app. Non dovrebbero essere in codice pubblicato, capture, log o URL.

    Il client mantiene il token in memoria. Lo invia solo al server che lo ha emesso. Le funzioni sottostanti negano i percorsi verso un altro server e HTTP reindirizza.

    2.2 Rispetta i nomi JSON

    Campo Ruolo
    AccountName, Password Credenziali inviate al server.
    Gettone Token finale da utilizzare nell'Autorizzazione: Bearer.
    Stato Fase del percorso MFA.
    CeremoniToken Riferimento temporaneo alla verifica corrente; non è un token di accesso.
    ProvisioningUri, ManualKey Informazioni riservate sulla configurazione TOTP, presenti alla registrazione.
    Codice, Messaggio In caso di errore Identificatore tecnico dell'errore e testo esplicativo.
    Codice nella query VerifyTotp Un codice TOTP di sei cifre, trasmesso come stringa.

    Le risposte di queste API utilizzano il PascalCase. Leggi Gettone, non Gettone, e CeremoniToken, non CerimoniaToken. Alcune risposte classiche possono contenere metadati come $type : I clienti qui sotto li ignorano.

    Il Contratto di Connessione Ospitata è un'eccezione: Ritorni Browser/Token access_token, token_type e expires_in. Non mischiare i due formati.

    2.3 L'intestazione Origin per MFA

    Strade MFA/VerifyCredentials, MFA/VerifyTotp e MFA/Annulla richiedi un corpo JSON e un'origine che sia in MFA: Origini.

    Cliente Valore dell'origine
    .http, C#, Python, PowerShell, file JavaScript in Node.js Invialo esplicitamente. In un test locale, https://localhost:44333 Adatto se questa origine è consentita dal server.
    JavaScript o TypeScript in un browser Il browser lo invia automaticamente: è l'origine del sito che esegue il codice. Lo script non può sostituirlo con quello di RestFrontage.

    Per un'applicazione web ospitata su https://application.exemple.fr chi chiama https://rest.exemple.fr, hanno https://application.exemple.fr in Mfa:Origins e nella politica CORS di RestFrontage. I due controlli hanno ruoli separati. Un'origine non ha una barra di percorso o di estremità.

    L'origine non è né una chiave API né una prova di identità. In un client non browser, questa intestazione può essere compilata dal programma; il server verifica sempre la password e la MFA. Vedi Origine nel browser e CORS.

    Per maggiori informazioni sulla MFA in Immersive, consulta la documentazione sull'autenticazione multifattore

    2.4 Funzioni comuni degli esempi

    Ogni variante prepara il client HTTP utilizzato nelle sezioni successive. SendAsync, invia, send_request e Send-Immersive sono utility in questo articolo, non funzioni fornite da una DLL Immersive.

    Per C#, crea un'app console .NET 8 o successivo ; Gli snippet sono usati nello stesso file di istruzioni di livello superiore, con gli usi impliciti abilitati. Per JavaScript, usa un modulo in un browser HTTPS o Node.js 22 o dopo. Le seguenti chiamate JavaScript possono essere utilizzate anche in TypeScript; la scheda TypeScript fornisce una variante tipizzata della preparazione.

    Python richiede Python 3.10 o successivo e Richieste. La conversione opzionale di una chiave API in token utilizza anch'essa Crittografia :

    python -m pip install requests cryptography
    
    

    Campioni PowerShell target PowerShell 7, inclusi RSA-OAEP-SHA256. Non prendono di mira PowerShell 5.1 di Windows.

    C#
    using System.Net.Http;
    using System.Security.Cryptography;
    using System.Text;
    using System.Text.Json;
    
    var server = new Uri("https://localhost:44333");
    var origin = server.GetLeftPart(UriPartial.Authority);
    using var http = new HttpClient(new HttpClientHandler
    {
        AllowAutoRedirect = false,
        UseCookies = false
    }) { Timeout = TimeSpan.FromSeconds(30) };
    
    async Task<JsonElement> SendAsync(
        HttpMethod method, string path, object? body = null,
        Dictionary<string, string>? headers = null)
    {
        if (server.Scheme != "https" || !string.IsNullOrEmpty(server.UserInfo)
            || !path.StartsWith('/') || path.StartsWith("//") || path.Contains('\\'))
            throw new ArgumentException("Adresse HTTPS et chemin local attendus.");
        var uri = new Uri(server, path);
        if (uri.GetLeftPart(UriPartial.Authority) != origin)
            throw new ArgumentException("Changement de serveur interdit.");
        using var request = new HttpRequestMessage(method, uri);
        request.Headers.Accept.ParseAdd("application/json");
        if (headers != null)
            foreach (var pair in headers) request.Headers.Add(pair.Key, pair.Value);
        if (body != null)
            request.Content = new StringContent(
                JsonSerializer.Serialize(body), Encoding.UTF8, "application/json");
        using var response = await http.SendAsync(request);
        if (!response.IsSuccessStatusCode)
            throw new HttpRequestException(
                "Appel refusé : HTTP " + (int)response.StatusCode,
                null, response.StatusCode);
        using var json = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
        return json.RootElement.Clone();
    }
    
    string ReadToken(JsonElement response)
    {
        if (!response.TryGetProperty("Token", out var value)
            || value.ValueKind != JsonValueKind.String
            || string.IsNullOrWhiteSpace(value.GetString()))
            throw new InvalidOperationException("Aucun token d'accès dans cette réponse.");
        return value.GetString()!;
    }
    var mfaHeaders = new Dictionary<string, string> { ["Origin"] = origin };
    
    JavaScript
    const server = new URL("https://localhost:44333");
    const origin = server.origin;
    // Hors navigateur seulement ; dans le navigateur, Origin est automatique.
    const mfaHeaders = typeof window === "undefined" ? { Origin: origin } : {};
    
    async function send(method, path, body = undefined, headers = {}) {
        if (server.protocol !== "https:" || server.username || server.password
            || !path.startsWith("/") || path.startsWith("//") || path.includes("\\"))
            throw new Error("Adresse HTTPS et chemin local attendus.");
        const url = new URL(path, server);
        if (url.origin !== origin) throw new Error("Changement de serveur interdit.");
        const response = await fetch(url, {
            method, credentials: "omit", redirect: "error", cache: "no-store",
            signal: AbortSignal.timeout(30000),
            headers: {
                Accept: "application/json",
                ...(body === undefined ? {} : { "Content-Type": "application/json" }),
                ...headers
            },
            ...(body === undefined ? {} : { body: JSON.stringify(body) })
        });
        if (!response.ok) throw new Error("Appel refusé : HTTP " + response.status);
        return await response.json();
    }
    
    function readToken(response) {
        if (typeof response.Token !== "string" || !response.Token)
            throw new Error("Aucun token d'accès dans cette réponse.");
        return response.Token;
    }
    
    Python
    import requests
    from urllib.parse import urljoin, urlsplit
    
    server = "https://localhost:44333"
    origin = server
    mfa_headers = {"Origin": origin}
    
    def send_request(method, path, body=None, headers=None):
        base = urlsplit(server)
        if (base.scheme != "https" or base.username or base.password
                or not path.startswith("/") or path.startswith("//") or "\\" in path):
            raise ValueError("Adresse HTTPS et chemin local attendus.")
        url = urljoin(server, path)
        if urlsplit(url).netloc != base.netloc:
            raise ValueError("Changement de serveur interdit.")
        response = requests.request(
            method, url, json=body,
            headers={"Accept": "application/json", **(headers or {})},
            timeout=30, allow_redirects=False
        )
        if not 200 <= response.status_code < 300:
            raise RuntimeError("Appel refusé : HTTP " + str(response.status_code))
        return response.json()
    
    def read_token(response):
        token = response.get("Token")
        if not isinstance(token, str) or not token:
            raise RuntimeError("Aucun token d'accès dans cette réponse.")
        return token
    
    PowerShell
    $server = [Uri]'https://localhost:44333'
    $origin = $server.GetLeftPart([UriPartial]::Authority)
    $mfaHeaders = @{ Origin = $origin }
    
    function Send-Immersive {
        param(
            [string]$Method, [string]$Path,
            [object]$Body = $null, [hashtable]$Headers = @{}
        )
        if ($server.Scheme -ne 'https' -or $server.UserInfo -or
            -not $Path.StartsWith('/') -or $Path.StartsWith('//') -or $Path.Contains('\')) {
            throw 'Adresse HTTPS et chemin local attendus.'
        }
        $uri = [Uri]::new($server, $Path)
        if ($uri.GetLeftPart([UriPartial]::Authority) -ne $origin) {
            throw 'Changement de serveur interdit.'
        }
        $allHeaders = @{ Accept = 'application/json' }
        foreach ($key in $Headers.Keys) { $allHeaders[$key] = $Headers[$key] }
        $arguments = @{
            Uri = $uri; Method = $Method; Headers = $allHeaders
            TimeoutSec = 30; MaximumRedirection = 0; ErrorAction = 'Stop'
        }
        if ($null -ne $Body) {
            $arguments.ContentType = 'application/json; charset=utf-8'
            $arguments.Body = ConvertTo-Json -InputObject $Body -Depth 8 -Compress
        }
        Invoke-RestMethod @arguments
    }
    function Read-ImmersiveToken {
        param([object]$Response)
        if ([string]::IsNullOrWhiteSpace($Response.Token)) {
            throw "Aucun token d'accès dans cette réponse."
        }
        $Response.Token
    }
    
    TypeScript
    const server = new URL("https://localhost:44333");
    const origin = server.origin;
    // Hors navigateur seulement ; dans le navigateur, Origin est automatique.
    const mfaHeaders: Record<string, string> = typeof window === "undefined" ? { Origin: origin } : {};
    
    async function send<T = any>(
        method: string, path: string, body?: unknown,
        headers: Record<string, string> = {}
    ): Promise<T> {
        if (server.protocol !== "https:" || server.username || server.password
            || !path.startsWith("/") || path.startsWith("//") || path.includes("\\"))
            throw new Error("Adresse HTTPS et chemin local attendus.");
        const url = new URL(path, server);
        if (url.origin !== origin) throw new Error("Changement de serveur interdit.");
        const response = await fetch(url, {
            method, credentials: "omit", redirect: "error", cache: "no-store",
            signal: AbortSignal.timeout(30000),
            headers: {
                Accept: "application/json",
                ...(body === undefined ? {} : { "Content-Type": "application/json" }),
                ...headers
            },
            ...(body === undefined ? {} : { body: JSON.stringify(body) })
        });
        if (!response.ok) throw new Error("Appel refusé : HTTP " + response.status);
        return await response.json() as T;
    }
    
    function readToken(response: { Token?: unknown }): string {
        if (typeof response.Token !== "string" || !response.Token)
            throw new Error("Aucun token d'accès dans cette réponse.");
        return response.Token;
    }
    
    type MfaReply =
        | { Status: "authenticated"; Token: string }
        | { Status: "totp_required"; CeremonyToken: string;
            ProvisioningUri: null; ManualKey: null }
        | { Status: "totp_enrollment_required"; CeremonyToken: string;
            ProvisioningUri: string; ManualKey: string };
    // Pour typer la réponse : await send<MfaReply>("POST", chemin, corps, mfaHeaders)
    

    Queste utility generano un errore per qualsiasi risposta non 2xx e non mostrano i corpi sensibili. Nella tua interfaccia, elabora lo stato HTTP e il Messaggio Errori JSON per visualizzare il testo appropriato. Alcuni rifiuti sono testo o non hanno corpo.

    Python utilizza lo store certificati riconosciuto tramite richieste. Se la tua azienda fornisce autorità interna, configura il suo certificato con REQUESTS_CA_BUNDLE. Non disabilitare il controllo TLS. Vedi Richieste di documentazione.

    2.5 Fornisci valori specifici per la tua applicazione

    Gli estratti per ogni modalità completano la preparazione sopra. Non sono un singolo programma da concatenare completamente: scegli una modalità di connessione e poi la chiamata autenticata.

    Valore C# / JavaScript / TypeScript Python PowerShell
    Accesso inserito accountName account_name $accountName
    Password inserita Parola d'Ordine Parola d'Ordine $password
    Codice TOTP inserito, come stringa Codice Codice $code
    Nome dell'applicazione fornito con la chiave API AppName app_name $appName
    Chiave API fornita dall'amministratore apiKey api_key $apiKey

    Queste variabili devono essere popolate dalla tua interfaccia o archivio segreto prima di eseguire lo snippet. Gli esempi .http contengono valori da sovrascrivere. Tieni questi file di test fuori dal repository se contengono segreti reali.

    3. Classic login e password

    3.1 Richiesta e risposta

    Il ricorso POST /api/Security/Users/Authenticate riceve un oggetto JSON contenente AccountName e Password. Questa non è un'autenticazione HTTP Basic: non è richiesta alcuna intestazione Basic.

    HTTP
    @server = https://localhost:44333
    @username = compte.exemple
    @password = REMPLACER_PAR_LE_MOT_DE_PASSE
    
    ### Mot de passe seul, si la politique l'autorise
    # @name authenticate
    POST {{server}}/api/Security/Users/Authenticate
    Content-Type: application/json
    Accept: application/json
    
    {
      "AccountName": "{{username}}",
      "Password": "{{password}}"
    }
    
    C#
    var authentication = await SendAsync(
        HttpMethod.Post, "/api/Security/Users/Authenticate",
        new { AccountName = accountName, Password = password });
    string token = ReadToken(authentication);
    
    JavaScript
    const authentication = await send(
        "POST", "/api/Security/Users/Authenticate",
        { AccountName: accountName, Password: password }
    );
    const token = readToken(authentication);
    
    Python
    authentication = send_request(
        "POST", "/api/Security/Users/Authenticate",
        {"AccountName": account_name, "Password": password}
    )
    token = read_token(authentication)
    
    PowerShell
    $body = @{ AccountName = $accountName; Password = $password }
    $authentication = Send-Immersive -Method POST -Path '/api/Security/Users/Authenticate' -Body $body
    $token = Read-ImmersiveToken $authentication
    

    La risposta positiva contiene Gettone e le informazioni utente in AuthenticatedUser. Ecco solo i campi utili per questo esempio:

    {
      "Token": "JWT_IMMERSIVE",
      "AuthenticatedUser": {
        "AccountName": "compte.exemple"
      }
    }
    
    

    Non richiede un campo Stato Su questa risposta classica. Inoltre, non dare per scontato che tutte le proprietà dell'utente siano presenti in tutti gli ambienti.

    In un file .http, i caratteri speciali nella password devono essere sfuggiti per formare un JSON valido. In un programma, lascia che il serializer JSON codifichi il valore; non costruire il corpo tramite concatenazione.

    3.2 Quando l'MFA è obbligatorio

    Con gli identificatori corretti, questa rotta restituisce HTTP 403 Quando è richiesta la MFA per l'account:

    {
      "Code": "mfa_required",
      "Message": "Utilisez un client compatible avec la MFA."
    }
    
    

    Questo non significa che OData non sia disponibile: devi completare una connessione abilitata alla MFA prima di chiamarlo.

    La sola password rimane utilizzabile quando la MFA è disabilitata o quando l'account è effettivamente esentabile nella modalità con esenzioni. La presenza di un'applicazione TOTP già configurata non rende di per sé obbligatoria la MFA: è la politica del server a decidere.

    Una password vuota segue l'impostazione del server esistente. Il client non dovrebbe modificare questa impostazione né considerare un valore vuoto come modo alternativo di connettersi.

    4. Accedi con MFA TOTP

    4.1 Scegli lo scopo

    Per un nuovo cliente, invia login-with-totp-or-enroll. Questo corso è adatto sia a un utente già registrato sia a un nuovo utente.

    Scopo Utilizzo
    login-with-totp-or-enroll Accesso completo: token immediato se consentito, TOTP se necessario, registrazione iniziale o riregistrazione consentita se necessario.
    login-with-totp Accedi senza una schermata di iscrizione. Se è richiesta la MFA, l'account deve già avere la verifica attiva.

    Questi valori indicano la capacità del customer journey; non permettono di disabilitare l'MFA scelto dall'amministratore. login-with-totp può quindi restituire un token immediatamente se la policy non richiede MFA per quell'account.

    4.2 Verifica le credenziali

    HTTP
    @server = https://localhost:44333
    @origin = https://localhost:44333
    @username = compte.exemple
    @password = REMPLACER_PAR_LE_MOT_DE_PASSE
    
    ### Étape 1 : lire Status avant de poursuivre
    # @name verifyCredentials
    POST {{server}}/api/Security/Mfa/VerifyCredentials
    Content-Type: application/json
    Accept: application/json
    Origin: {{origin}}
    
    {
      "Credentials": {
        "AccountName": "{{username}}",
        "Password": "{{password}}"
      },
      "Purpose": "login-with-totp-or-enroll"
    }
    
    C#
    var step = await SendAsync(
        HttpMethod.Post, "/api/Security/Mfa/VerifyCredentials",
        new {
            Credentials = new { AccountName = accountName, Password = password },
            Purpose = "login-with-totp-or-enroll"
        }, mfaHeaders);
    string status = step.GetProperty("Status").GetString()!;
    
    JavaScript
    const step = await send(
        "POST", "/api/Security/Mfa/VerifyCredentials",
        {
            Credentials: { AccountName: accountName, Password: password },
            Purpose: "login-with-totp-or-enroll"
        }, mfaHeaders
    );
    const status = step.Status;
    
    Python
    step = send_request(
        "POST", "/api/Security/Mfa/VerifyCredentials",
        {
            "Credentials": {"AccountName": account_name, "Password": password},
            "Purpose": "login-with-totp-or-enroll"
        }, mfa_headers
    )
    status = step["Status"]
    
    PowerShell
    $body = @{
        Credentials = @{ AccountName = $accountName; Password = $password }
        Purpose = 'login-with-totp-or-enroll'
    }
    $step = Send-Immersive -Method POST -Path '/api/Security/Mfa/VerifyCredentials' -Body $body -Headers $mfaHeaders
    $status = $step.Status
    

    Per maggiori informazioni sulla MFA in Immersive, consulta la documentazione sull'autenticazione multifattore

    4.3 Affrontare le tre possibili risposte

    Caso 1 — Collegamento completato. Se il server consente la connessione senza un passaggio aggiuntivo:

    {
      "Status": "authenticated",
      "Token": "JWT_IMMERSIVE"
    }
    
    

    Uso diretto Gettone : ReadToken (step) in C#, readToken(step) in JavaScript/TypeScript, read_token(passo) in Python, Read-ImmersiveToken $step in PowerShell. Non chiamare VerifyTotp : Non è stato emesso alcun CeremonyToken.

    Caso 2 — TOTP richiesto. L'account ha già la verifica:

    {
      "Status": "totp_required",
      "CeremonyToken": "REFERENCE_TEMPORAIRE",
      "ProvisioningUri": null,
      "ManualKey": null
    }
    
    

    Tieni CeremoniToken in memoria e visualizzare l'inserimento del codice telefonico. Non sono ancora stati emessi gettoni di accesso.

    Caso 3 — Registrazione richiesta. L'account deve impostare la sua verifica:

    {
      "Status": "totp_enrollment_required",
      "CeremonyToken": "REFERENCE_TEMPORAIRE",
      "ProvisioningUri": "otpauth://totp/Immersive:compte.exemple?secret=SECRET_FICTIF&issuer=Immersive&algorithm=SHA1&digits=6&period=30",
      "ManualKey": "SECRET_FICTIF"
    }
    
    

    L'URI e il segreto sopra sono illustrativi. Nella tua interfaccia:

    1. Genera A livello locale il codice QR rappresenta esattamente ProvisioningUri.
    2. Proposta ManualKey come soluzione di input manuale se la scansione non è possibile.
    3. L'utente aggiunge l'account in Microsoft Authenticator o in un'altra app abilitata a TOTP.
    4. Inserisce il primo codice a sei cifre.
    5. Invia questo codice con il stesso Gettone di Cerimonia per verificare Totp.

    Non passare ProvisioningUri a un servizio web di terze parti per la generazione di codici QR. Questo URI contiene il segreto per calcolare codici futuri. Cancella, insieme a ManualKey e al riferimento temporaneo, dopo successo o abbandono.

    A TOTP è una password uniuso calcolata a partire da un segreto e dal tempo. Usi immersivi qui Sei cifre, SHA-1 e un periodo di 30 secondi. Il telefono può calcolare il codice senza Internet; devi sempre riuscire a raggiungere RestFrontage per verificarlo. Il centralino è il RFC 6238.

    sequenceDiagram
        participant U as Utilisateur
        participant A as Application cliente
        participant R as RestFrontage
        participant T as Application TOTP
        U->>A: Identifiant et mot de passe
        A->>R: VerifyCredentials
        alt Connexion immédiatement autorisée
            R-->>A: authenticated et Token
        else Vérification supplémentaire
            R-->>A: CeremonyToken et étape attendue
            opt Inscription ou réenregistrement autorisé
                A-->>U: QR code et clé manuelle
                U->>T: Ajouter le compte
            end
            T-->>U: Code à six chiffres
            U->>A: Saisir le code
            A->>R: VerifyTotp avec CeremonyToken et Code
            alt Code accepté
                R-->>A: authenticated et Token
            else Code refusé
                R-->>A: HTTP 400 et Message
                A-->>U: Corriger la saisie ou attendre le prochain code
            end
        end
    

    4.4 Conferma il codice e recupera il token

    Questo passo è il seguente solo dopo totp_required o totp_enrollment_required. La variabile Codice è una stringa derivata dall'input dell'utente, ad esempio "012345". Non convertirlo in un numero: lo zero iniziale fa parte del codice.

    HTTP
    @totp = 012345
    
    ### Étape 2 : seulement si l'étape 1 fournit CeremonyToken
    # @name authenticateMfa
    POST {{server}}/api/Security/Mfa/VerifyTotp
    Content-Type: application/json
    Accept: application/json
    Origin: {{origin}}
    
    {
      "CeremonyToken": "{{verifyCredentials.response.body.$.CeremonyToken}}",
      "Code": "{{totp}}"
    }
    
    C#
    if (status is not ("totp_required" or "totp_enrollment_required"))
        throw new InvalidOperationException("Aucune vérification TOTP en attente.");
    if (code.Length != 6 || code.Any(c => c is < '0' or > '9'))
        throw new ArgumentException("Saisissez six chiffres.");
    var authentication = await SendAsync(
        HttpMethod.Post, "/api/Security/Mfa/VerifyTotp",
        new { CeremonyToken = step.GetProperty("CeremonyToken").GetString(), Code = code },
        mfaHeaders);
    string token = ReadToken(authentication);
    
    JavaScript
    if (!["totp_required", "totp_enrollment_required"].includes(status))
        throw new Error("Aucune vérification TOTP en attente.");
    if (!/^[0-9]{6}$/.test(code)) throw new Error("Saisissez six chiffres.");
    const authentication = await send(
        "POST", "/api/Security/Mfa/VerifyTotp",
        { CeremonyToken: step.CeremonyToken, Code: code }, mfaHeaders
    );
    const token = readToken(authentication);
    
    Python
    if status not in ("totp_required", "totp_enrollment_required"):
        raise ValueError("Aucune vérification TOTP en attente.")
    if len(code) != 6 or any(c not in "0123456789" for c in code):
        raise ValueError("Saisissez six chiffres.")
    authentication = send_request(
        "POST", "/api/Security/Mfa/VerifyTotp",
        {"CeremonyToken": step["CeremonyToken"], "Code": code}, mfa_headers
    )
    token = read_token(authentication)
    
    PowerShell
    if ($status -notin @('totp_required', 'totp_enrollment_required')) {
        throw 'Aucune vérification TOTP en attente.'
    }
    if ($code -cnotmatch '^[0-9]{6}$') { throw 'Saisissez six chiffres.' }
    $body = @{ CeremonyToken = $step.CeremonyToken; Code = $code }
    $authentication = Send-Immersive -Method POST -Path '/api/Security/Mfa/VerifyTotp' -Body $body -Headers $mfaHeaders
    $token = Read-ImmersiveToken $authentication
    

    Per entrambe le connessioni con Purpose, la risposta efficace è:

    {
      "Status": "authenticated",
      "Token": "JWT_IMMERSIVE"
    }
    
    

    Questo vale anche dopo la conferma di una registrazione avviata con login-with-totp-or-enroll : La registrazione riuscita completa l'accesso e fornisce il token. Non è necessario ricominciare immediatamente con la password.

    4.5 Scadenza, ritentativo e annullamento

    Un assegno è valido Cinque minuti. Il suo riferimento può essere usato con successo solo una volta. Un codice già accettato non può essere riutilizzato per un'altra verifica.

    Dopo un codice rifiutato, visualizza l'errore e consente una nuova voce con lo stesso CeremonyToken finché la verifica rimane valida. Non ritentare automaticamente i tentativi di testa. Dopo Cinque codici errati, la verifica dell'account è bloccata per Quindici minuti ; Ricominciare con la password non resetta il timer.

    Se il riferimento è scaduto, la politica è cambiata o l'utente è stato cancellato, ricomincia su VerifyCredentials. Dopo che l'amministratore ha reimpostato un telefono, login-with-totp-or-enroll presenta la nuova registrazione purché l'autorizzazione di ri-registrazione sia valida. Non sono necessari codici di recupero aggiuntivi.

    Per annullare esplicitamente un controllo in sospeso:

    POST {{server}}/api/Security/Mfa/Cancel
    Content-Type: application/json
    Origin: {{origin}}
    
    {
      "CeremonyToken": "{{verifyCredentials.response.body.$.CeremonyToken}}"
    }
    
    

    La risposta è { "Status": "cancellato" }. Poi cancella i dati temporanei dalla tua interfaccia. Cancella non è una disconnessione e non revoca un token finale già emesso.

    4.6 Altri scopi non sono le modalità di connessione

    Valore Scopo Possibile risultato dopo la verifica
    Iscriviti Configura la verifica dell'account in un percorso di gestione autorizzato. Stato: registrato ; Nessun token di accesso.
    admin Conferma un'operazione di gestione protetta. Stato: autorizzato e ProofToken ; questa prova non è un Portatore di Sé stesso.
    Gestire Visualizza il metodo di verifica dell'account. Stato: credenziali e Credenziali ; Nessun token di accesso.
    elimina Rimuovere i mezzi di verifica quando consentito dalla politica. Stato: eliminato ; Rifiutato se l'MFA è ancora richiesto.
    solo credenziali Il nome usato dal classico viaggio con password. Utilizzo Utenti/Autenticazione per questo percorso; non farne uno scopo per il tuo cliente MFA.

    Per ottenere un token con MFA, la tua integrazione utilizza i due scopi di connessione descritti sopra. Non serve il Nome, che non fa più parte della query.

    5. Autenticazione della chiave API

    5.1 Chiama direttamente le API

    Ottieni una chiave valida e è Nome dell'applicazione. La chiave deve essere entro il suo periodo di validità e l'account associato deve essere autorizzato ad accedere. Il nome dell'applicazione viene confrontato in base a case insensitive; il valore della chiave è distinto a maiuscole e minuscoli.

    Per maggiori informazioni, consulta la nostra documentazione sulla gestione delle chiavi API in Rest Frontage

    HTTP
    @server = https://localhost:44333
    @appName = Supervision atelier
    @apiKey = REMPLACER_PAR_LA_CLE_API
    
    ### Authentification directe par clé API
    GET {{server}}/api/Security/Mfa/Context
    Accept: application/json
    x-app-name: {{appName}}
    x-api-key: {{apiKey}}
    
    C#
    var keyHeaders = new Dictionary<string, string>
    {
        ["x-app-name"] = appName,
        ["x-api-key"] = apiKey
    };
    var account = await SendAsync(
        HttpMethod.Get, "/api/Security/Mfa/Context", headers: keyHeaders);
    string? name = account.GetProperty("AccountName").GetString();
    
    JavaScript
    // Node.js uniquement : la clé reste côté serveur.
    const keyHeaders = { "x-app-name": appName, "x-api-key": apiKey };
    const account = await send(
        "GET", "/api/Security/Mfa/Context", undefined, keyHeaders
    );
    const name = account.AccountName;
    
    Python
    key_headers = {"x-app-name": app_name, "x-api-key": api_key}
    account = send_request(
        "GET", "/api/Security/Mfa/Context", headers=key_headers
    )
    name = account["AccountName"]
    
    PowerShell
    $keyHeaders = @{ 'x-app-name' = $appName; 'x-api-key' = $apiKey }
    $account = Send-Immersive -Method GET -Path '/api/Security/Mfa/Context' -Headers $keyHeaders
    $name = $account.AccountName
    

    La risposta in questo esempio contiene AccountName, non un token. La chiave ha autenticato direttamente la richiesta. Puoi inviare le stesse due intestazioni alla tua WebAPI o alla rotta OData.

    Il Sentiero MFA/Contesto Questo viene usato qui solo per verificare l'account collegato. Questo GET non richiede un codice MFA.

    Non mandare uno vecchio nello stesso momento Autorizzazione: Titolare oppure un cookie di autenticazione: utilizzare chiaramente o le intestazioni della chiave API o il portatore risultante. Una chiave API non viene trasmessa scrivendo Permesso: Portatore CLE_API.

    6. Utilizzo del token con WebAPI e OData

    6.1 Chiamata autenticata

    Che sia tramite password, MFA o API key, il token finale viene inviato a Autorizzazione: Titolare.

    HTTP
    ### Après Users/Authenticate
    GET {{server}}/api/Security/Mfa/Context
    Accept: application/json
    Authorization: Bearer {{authenticate.response.body.$.Token}}
    
    ### Après VerifyTotp
    GET {{server}}/api/Security/Mfa/Context
    Accept: application/json
    Authorization: Bearer {{authenticateMfa.response.body.$.Token}}
    
    ### Si VerifyCredentials a répondu authenticated directement
    GET {{server}}/api/Security/Mfa/Context
    Accept: application/json
    Authorization: Bearer {{verifyCredentials.response.body.$.Token}}
    
    ### Après Token/Renew avec une clé API
    GET {{server}}/api/Security/Mfa/Context
    Accept: application/json
    Authorization: Bearer {{authenticateApiKey.response.body.$.Token}}
    
    C#
    var bearerHeaders = new Dictionary<string, string>
    {
        ["Authorization"] = "Bearer " + token
    };
    var result = await SendAsync(
        HttpMethod.Get, "/api/Security/Mfa/Context", headers: bearerHeaders);
    string? accountNameFromToken = result.GetProperty("AccountName").GetString();
    
    JavaScript
    const result = await send(
        "GET", "/api/Security/Mfa/Context", undefined,
        { Authorization: "Bearer " + token }
    );
    const accountNameFromToken = result.AccountName;
    
    Python
    result = send_request(
        "GET", "/api/Security/Mfa/Context",
        headers={"Authorization": "Bearer " + token}
    )
    account_name_from_token = result["AccountName"]
    
    PowerShell
    $bearerHeaders = @{ Authorization = 'Bearer ' + $token }
    $result = Send-Immersive -Method GET -Path '/api/Security/Mfa/Context' -Headers $bearerHeaders
    $accountNameFromToken = $result.AccountName
    

    Per OData, mantieni lo stesso header e sostituisci il percorso con una raccolta pubblicata dal tuo server. Nome della collezione è un luogo da sostituire, non una strada fornita da questo articolo:

    GET {{server}}/odata/NomDeCollection?$top=10
    Accept: application/json
    Authorization: Bearer {{authenticateMfa.response.body.$.Token}}
    
    

    Un token valido dimostra l'autenticazione. I diritti degli account, i filtri organizzativi e i permessi aziendali rimangono applicabili. L'MFA non trasforma un utente ordinario in un amministratore.

    6.2 Valida, scadenza e rinnovo

    GET /api/Security/Token/Validate, con il Portatore ti permette di controllare la sessione. Il successo è HTTP 202 accettato, con un oggetto che include Oggetto e IsAdministrator. Non testare solo HTTP 200.

    GET {{server}}/api/Security/Token/Validate
    Accept: application/json
    Authorization: Bearer {{authenticateMfa.response.body.$.Token}}
    
    

    La data di scadenza di un JWT è indicata nel suo campo Esperienza. Leggerlo dal lato client serve a organizzare una riconnessione; non sostituisce mai la validazione del token da parte del server.

    Le rotte classiche e MFA in questa guida non restituiscono un token di aggiornamento. Quando una sessione non viene più accettata, riavvia il percorso per la modalità autenticazione.

    Il Percorso Storico Token/Rinnova può rinnovare una sessione ancora valida, con lo stesso corpo criptato di prima, ma inviando il Portatore comune invece delle intestazioni della chiave API. Non ti permette di recuperare una sessione scaduta né di bypassare un requisito MFA. La nuova risposta deve sostituire il token che il client detiene.

    Un cambiamento nella policy MFA o un reset può rendere una sessione inutilizzabile prima della scadenza. Quindi gestisci sempre i rifiuti del server, anche se l'esperienza indica ancora una data futura.

    7. Usa una chiave API con WebAPI e OData

    Una chiave API autentica direttamente ogni richiesta su RestFrontage. La tua applicazione passa sempre attraverso le seguenti due intestazioni:

    • x-app-name : Il nome dell'applicazione associata alla chiave.
    • x-api-key : Il valore della chiave.

    Non è necessario alcun accesso o recupero del token precedente. Non si chiamano le modalità di login con password o verifica TOTP.

    7.1 Fai chiamate

    Gli esempi seguenti eseguono due interrogatorie:

    1. Una chiamata WebAPI a /api/Security/MFA/Contesto, per verificare il nome dell'account associato alla chiave. Nonostante il nome, questa via non richiede alcun codice MFA.
    2. Una chiamata OData a /odata/EquipmentInstances?$top=10, per richiedere fino a dieci apparecchiature accessibili a questo account.

    Le varianti programmate riutilizzano le funzioni comuni presentate all'inizio di questo articolo. Compila il nome dell'applicazione e della sua chiave con i valori forniti dal tuo amministratore.

    HTTP
    @server = https://localhost:44333
    @appName = MonApplication
    @apiKey = REMPLACER_PAR_LA_CLE_API
    
    ### WebAPI : vérifier le compte connecté
    GET {{server}}/api/Security/Mfa/Context
    Accept: application/json
    x-app-name: {{appName}}
    x-api-key: {{apiKey}}
    
    ### OData : lire jusqu'à dix équipements
    GET {{server}}/odata/EquipmentInstances?$top=10
    Accept: application/json
    x-app-name: {{appName}}
    x-api-key: {{apiKey}}
    
    C#
    var keyHeaders = new Dictionary<string, string>
    {
        ["x-app-name"] = appName,
        ["x-api-key"] = apiKey
    };
    
    // WebAPI
    var account = await SendAsync(
        HttpMethod.Get,
        "/api/Security/Mfa/Context",
        headers: keyHeaders);
    
    // OData
    var equipments = await SendAsync(
        HttpMethod.Get,
        "/odata/EquipmentInstances?$top=10",
        headers: keyHeaders);
    
    JavaScript
    // Node.js uniquement : conserver la clé côté serveur.
    // Cet extrait s'utilise également en TypeScript.
    const keyHeaders = {
        "x-app-name": appName,
        "x-api-key": apiKey
    };
    
    // WebAPI
    const account = await send(
        "GET",
        "/api/Security/Mfa/Context",
        undefined,
        keyHeaders
    );
    
    // OData
    const equipments = await send(
        "GET",
        "/odata/EquipmentInstances?$top=10",
        undefined,
        keyHeaders
    );
    
    Python
    key_headers = {
        "x-app-name": app_name,
        "x-api-key": api_key
    }
    
    # WebAPI
    account = send_request(
        "GET",
        "/api/Security/Mfa/Context",
        headers=key_headers
    )
    
    # OData
    equipments = send_request(
        "GET",
        "/odata/EquipmentInstances?$top=10",
        headers=key_headers
    )
    
    PowerShell
    $keyHeaders = @{
        'x-app-name' = $appName
        'x-api-key' = $apiKey
    }
    
    # WebAPI
    $account = Send-Immersive -Method GET -Path '/api/Security/Mfa/Context' -Headers $keyHeaders
    
    # OData
    $equipments = Send-Immersive -Method GET -Path '/odata/EquipmentInstances?$top=10' -Headers $keyHeaders
    

    Ogni risposta contiene il risultato dell'operazione richiesta: informazioni contabili per la prima chiamata, dati OData per la seconda. Non c'è alcun gettone da estrarre da queste risposte.

    Per chiamare una route diversa, mantenere entrambi gli intestamenti e adattare il percorso, il metodo HTTP e il corpo secondo l'operazione. Parametri OData, come $filter, $select oppure $orderby, rimangono utilizzabili quando la collezione li supporta.

    7.2 Tieni le intestazioni su ogni richiesta

    L'autenticazione tramite chiave API viene effettuata in ogni chiamata. Una prima richiesta riuscita non ti esenta dal passare le intestazioni nelle chiamate successive.

    Questo vale anche per le richieste di paging e le chiamate ad altre collezioni sullo stesso server. Verifica che un indirizzo di paging appartenga al tuo server RestFrontage prima di passargli la chiave.

    Non aggiungere un'intestazione Autorizzazione: Titolare a queste chiamate e non inviare un cookie corrispondente a un'altra sessione utente.

    7.3 I diritti rimangono quelli dell'account associato

    La chiave API fornisce accesso alle operazioni consentite per il suo account associato. Le restrizioni organizzative e i permessi aziendali rimangono applicabili.

    Una chiave valida non garantisce l'accesso a tutti i dati o alle operazioni. Ad esempio, un account autorizzato a visualizzare l'apparecchiatura non ha automaticamente il diritto di modificarla.

    La chiave deve anche rimanere valida e l'account associato deve poter accedere. Una chiave scaduta, disabilitata o eliminata non può più autenticare le chiamate.

    8. Errori di lettura senza bloccare l'utente

    Risposta Interpretazione e azione
    400 su Utenti/Autenticazione Richiesta non valida; controlla il corpo e qualsiasi impostazione di password vuota.
    401 su Utenti/Autenticazione Credenziali rifiutate o account non disponibili. Presenta un errore senza tentare automaticamente di riprovare.
    403, Codice: mfa_required Sul login classico Usa il percorso MFA o il login ospitato.
    403 su una tratta MFA, a volte senza corpo JSON Controlla Origin e MFA:Origins. Nel browser, controlla anche CORS.
    415 su una tratta MFA Invia Tipo di contenuto: application/json.
    400, Codice: mfa_verification_failed Messaggio di lettura: Codice errato, già usato, riferimento, blocco o configurazione scaduti, non disponibile.
    400, Codice: mfa_enrollment_unavailable L'autorizzazione di nuova registrazione non è più utilizzabile; chiedi all'amministratore di rinnovare l'azione.
    401 con una chiave API Chiave non valida, fuori validità o token derivato revocato; controlla anche x-app-name.
    403 con una chiave API valida Il suo account collegato può essere assente, disattivato, scaduto o bloccato.
    401 su una WebAPI o OData con portatore Token assente, scaduto, revocato o diventato incompatibile con la policy; riconnettere il client.
    403 Su un'attività commerciale L'identità è riconosciuta ma l'operazione non è autorizzata; verifica i diritti.
    503 Un controllo di sicurezza è temporaneamente non disponibile; mostra l'indisponibilità e programma un nuovo processo differito.

    Non tutti i rifiuti hanno lo stesso formato. Il tuo cliente dovrebbe controllare lo stato HTTP prima di tentare di leggere un JSON.

    Se c'è un guasto di rete durante la conferma MFA, non presumere che la richiesta non sia stata eseguita: il server potrebbe aver consumato il riferimento senza che la risposta raggiungesse il client. Riprova la connessione se necessario e attendi il prossimo codice dal telefono.

    9. Tour storici e collegamento ospitato

    9.1 Sicurezza/Autenticazione e SSO

    POST /api/Security/Authenticate è un vecchio ingresso che riceve un stringa JSON cifrata, non l'oggetto in chiaro AccountName/Password mostrato qui. Il risultato di ciò è ancora soggetto alla policy MFA. Per una nuova integrazione diretta con password, usa Utenti/Autenticazione o percorsi MFA.

    Il login SSO è un percorso separato, eseguito con il fornitore di identità configurato presso la sede del cliente. Inviare un login e una password agli Utenti/Autenticati non trasforma la chiamata in un SSO. Un token SSO valido mantiene i diritti dell'account; l'MFA di questo percorso è responsabilità del fornitore configurato.

    9.2 Riutilizza le schermate del server

    Se non vuoi approfondire le interazioni descritte nella sezione TOTP, usa il percorso presentato nell'articolo Integra l'autenticazione immersiva e l'MFA nelle tue applicazioni web e .NET.

    Aperture clienti Browser/Autorizza con un indirizzo di ritorno dichiarato, uno stato e un PKCE. Riceve un codice temporaneo e poi lo riscatta tramite Browser/Token. Non chiama VerifyCredentials né VerifyTotp stesso. Il WPF può visualizzare questa connessione nel browser esterno o nella sua finestra WebView2.

    Scarica esempi web di WPF e login ospitato

    Questo download è per il percorso con l'interfaccia server. Esempi di chiamate dirette con password, MFA e chiave API si trovano nelle schede di questo articolo.

    Integra l'autenticazione immersiva

    10. Verifica la tua integrazione

    Controlla i casi che corrispondono alla tua domanda:

    • Un account autorizzato ad accedere solo con password.
    • Un account già registrato e soggetto a MFA.
    • Un account soggetto a MFA che imposta la verifica per la prima volta.
    • Una riregistrazione consentita dopo il reset.
    • Un codice TOTP errato, un codice già usato, una cancellazione e una referenza scaduta.
    • Una chiave API valida, una chiave scaduta e un account associato disabilitato.
    • Una chiamata riuscita, un gettone rifiutato e un'operazione proibita dai diritti.

    Il cliente deve distinguere tra una fase MFA in corso, Una connessione terminata e Un'attività commerciale autorizzata. Questi sono tre esiti diversi.