Postupy: Zápis tokenprovideru pomocí funkce Azure

Poznámka:

Tato verze Preview je poskytována bez smlouvy o úrovni služeb a nedoporučuje se pro produkční úlohy. Některé funkce se nemusí podporovat nebo mohou mít omezené možnosti.

V rozhraní Fluid Framework zodpovídají za vytváření a podepisování tokenů, které @fluidframework/azure-client používají k odesílání požadavků do služby Azure Fluid Relay. Fluid Framework poskytuje jednoduchý nezabezpečený TokenProvider pro účely vývoje, aptly s názvem InsecureTokenProvider. Každá služba Fluid musí implementovat vlastního tokenProvidera na základě požadavků na ověřování a zabezpečení konkrétní služby.

Každému vytvořenému prostředku Azure Fluid Relay se přiřadí ID tenanta a vlastní jedinečný tajný klíč tenanta. Tajný klíč je sdílený tajný klíč. Vaše aplikace nebo služba ji zná a služba Azure Fluid Relay ji zná. TokenProviders musí znát tajný klíč pro podepsání požadavků, ale tajný klíč nemůže být součástí klientského kódu.

Implementace funkce Azure Functions pro podepisování tokenů

Jednou z možností vytvoření zprostředkovatele zabezpečeného tokenu je vytvoření koncového bodu HTTPS a vytvoření implementace TokenProvider, která do daného koncového bodu provádí ověřené požadavky HTTPS pro načtení tokenů. Tato cesta umožňuje uložit tajný klíč tenanta do zabezpečeného umístění, jako je Azure Key Vault.

Kompletní řešení má dvě části:

  1. Koncový bod HTTPS, který přijímá požadavky a vrací tokeny Azure Fluid Relay.
  2. Implementace ITokenProvider, která přijímá adresu URL koncového bodu a pak odesílá požadavky na tento koncový bod k načtení tokenů.

Vytvoření koncového bodu pro vašeho poskytovatele tokenů pomocí Azure Functions

Použití Služby Azure Functions představuje rychlý způsob vytvoření takového koncového bodu HTTPS.

Tento příklad ukazuje, jak vytvořit vlastní funkci Azure HTTPTrigger, která načte token předáním klíče tenanta.

import { AzureFunction, Context, HttpRequest } from "@azure/functions";
import { ScopeType } from "@fluidframework/azure-client";
import { generateToken } from "@fluidframework/azure-service-utils";

// NOTE: retrieve the key from a secure location.
const key = "myTenantKey";

const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> {
    // tenantId, documentId, userId and userName are required parameters
    const tenantId = (req.query.tenantId || (req.body && req.body.tenantId)) as string;
    const documentId = (req.query.documentId || (req.body && req.body.documentId)) as string | undefined;
    const userId = (req.query.userId || (req.body && req.body.userId)) as string;
    const userName = (req.query.userName || (req.body && req.body.userName)) as string;
    const scopes = (req.query.scopes || (req.body && req.body.scopes)) as ScopeType[];

    if (!tenantId) {
        context.res = {
            status: 400,
            body: "No tenantId provided in query params",
        };
        return;
    }

    if (!key) {
        context.res = {
            status: 404,
            body: `No key found for the provided tenantId: ${tenantId}`,
        };
        return;
    }

    let user = { name: userName, id: userId };

    // Will generate the token and returned by an ITokenProvider implementation to use with the AzureClient.
    const token = generateToken(
        tenantId,
        documentId,
        key,
        scopes ?? [ScopeType.DocRead, ScopeType.DocWrite, ScopeType.SummaryWrite],
        user
    );

    context.res = {
        status: 200,
        body: token
    };
};

export default httpTrigger;

Funkce generateToken nalezená v @fluidframework/azure-service-utils balíčku vygeneruje token pro daného uživatele, který je podepsaný pomocí tajného klíče tenanta. Tato metoda umožňuje vrácení tokenu klientovi bez vystavení tajného klíče. Místo toho se token vygeneruje na straně serveru pomocí tajného kódu pro poskytnutí omezeného přístupu k danému dokumentu. Následující příklad ITokenProvider provádí požadavky HTTP na tuto funkci Azure Functions, aby načetly tokeny.

Nasazení funkce Azure Functions

Azure Functions je možné nasadit několika způsoby. Další informace najdete v části Nasazení v dokumentaci ke službě Azure Functions, kde najdete další informace o nasazení služby Azure Functions.

Implementace tokenprovideru

TokenProviders lze implementovat mnoha způsoby, ale musí implementovat dvě samostatná volání rozhraní API: fetchOrdererToken a fetchStorageToken. Tato rozhraní API zodpovídají za načítání tokenů pro objednávku tekutin a služby úložiště. Obě funkce vrací TokenResponse objekty představující hodnotu tokenu. Modul runtime Fluid Framework volá tato dvě rozhraní API podle potřeby k načtení tokenů. Všimněte si, že zatímco kód aplikace používá k navázání připojení ke službě Azure Fluid Relay pouze jeden koncový bod služby, klient Azure interně ve spojení se službou přeloží jeden koncový bod na dvojici koncových bodů objednávek a koncového bodu úložiště. Tyto dva koncové body se používají od tohoto okamžiku pro danou relaci, což je důvod, proč je potřeba implementovat dvě samostatné funkce pro načtení tokenů, jednu pro každou z nich.

Aby se zajistilo, že je tajný klíč tenanta zabezpečený, je uložený v zabezpečeném back-endovém umístění a je přístupný jenom z funkce Azure Functions. Pokud chcete načíst tokeny, musíte provést požadavek na nasazenou GET funkci Azure Functions a zadat a userIDtenantIDuserNamedocumentId/.POST Funkce Azure Functions zodpovídá za mapování mezi ID tenanta a tajným klíčem tenanta, aby token vygeneroval a podepisoval.

Následující ukázková implementace zpracovává provádění těchto požadavků do vaší funkce Azure Functions. K provádění požadavků HTTP používá knihovnu axios . K vytvoření požadavku HTTP z kódu serveru můžete použít jiné knihovny nebo přístupy. Tato konkrétní implementace je také poskytována jako export z @fluidframework/azure-client balíčku.

import { ITokenProvider, ITokenResponse } from "@fluidframework/routerlicious-driver";
import axios from "axios";
import { AzureMember } from "./interfaces";

/**
 * Token Provider implementation for connecting to an Azure Function endpoint for
 * Azure Fluid Relay token resolution.
 */
export class AzureFunctionTokenProvider implements ITokenProvider {
    /**
     * Creates a new instance using configuration parameters.
     * @param azFunctionUrl - URL to Azure Function endpoint
     * @param user - User object
     */
    constructor(
        private readonly azFunctionUrl: string,
        private readonly user?: Pick<AzureMember, "userId" | "userName" | "additionalDetails">,
    ) { }

    public async fetchOrdererToken(tenantId: string, documentId?: string): Promise<ITokenResponse> {
        return {
            jwt: await this.getToken(tenantId, documentId),
        };
    }

    public async fetchStorageToken(tenantId: string, documentId: string): Promise<ITokenResponse> {
        return {
            jwt: await this.getToken(tenantId, documentId),
        };
    }

    private async getToken(tenantId: string, documentId: string | undefined): Promise<string> {
        const response = await axios.get(this.azFunctionUrl, {
            params: {
                tenantId,
                documentId,
                userId: this.user?.userId,
                userName: this.user?.userName,
                additionalDetails: this.user?.additionalDetails,
            },
        });
        return response.data as string;
    }
}

Přidání efektivity a zpracování chyb

Jedná se AzureFunctionTokenProvider o jednoduchou implementaci, která TokenProvider by měla být považována za výchozí bod při implementaci vlastního zprostředkovatele tokenu. Pro implementaci zprostředkovatele tokenů připravených pro produkční prostředí byste měli zvážit různé scénáře selhání, které poskytovatel tokenu potřebuje zpracovat. Implementace například nedokáže zpracovat situace odpojení sítě, AzureFunctionTokenProvider protože token neukládá do mezipaměti na straně klienta.

Když se kontejner odpojí, správce připojení se pokusí získat nový token od TokenProvideru před opětovným připojením ke kontejneru. Když je síť odpojená, požadavek na získání rozhraní API se fetchOrdererToken nezdaří a vyvolá chybu, která se neopakuje. To zase vede k vyřazení kontejneru a k opětovnému připojení ani v případě opětovného navázání síťového připojení.

Potenciálním řešením tohoto problému odpojení je ukládání platných tokenů do mezipaměti ve službě Window.localStorage. Při ukládání tokenu do mezipaměti kontejner načte platný uložený token místo vytvoření požadavku na získání rozhraní API při odpojení sítě. Všimněte si, že platnost místně uloženého tokenu může vypršet po určité době a k získání nového platného tokenu byste stále museli vytvořit požadavek rozhraní API. V takovém případě by bylo potřeba další zpracování chyb a logika opakování, aby se kontejner po jednom neúspěšném pokusu nepovedl.

Způsob implementace těchto vylepšení je zcela na vás a na požadavcích vaší aplikace. Všimněte si, že u řešení tokenů localStorage se také ve vaší aplikaci zobrazí vylepšení výkonu, protože odebíráte síťový požadavek na každé getContainer volání.

Ukládání tokenů do mezipaměti s podobným localStorage způsobem může mít vliv na zabezpečení a při rozhodování o tom, jaké řešení je vhodné pro vaši aplikaci, záleží na vašem uvážení. Bez ohledu na to, jestli implementujete ukládání do mezipaměti tokenů, měli byste přidat logiku fetchOrdererToken zpracování chyb a opakování, fetchStorageToken aby kontejner nebyl uvolněn po jednom neúspěšném volání. Zvažte například zabalení volání getToken do bloku blokem trycatch , který se opakuje a vyvolá chybu až po zadaném počtu opakování.

Viz také