Dela via


Implementera OAuth 2.0 i Windows-appar

Med OAuth2Manager- i Windows App SDK kan skrivbordsprogram som WinUI 3 sömlöst utföra OAuth 2.0-auktorisering i Windows. OAuth2Manager-API:et tillhandahåller inte API:er för implicita autentiseringsuppgifter för begäran och resursägare på grund av de säkerhetsproblem som detta medför. Använd auktoriseringskodens beviljandetyp med Proof Key för Kodutbyte (PKCE). Mer information finns i PKCE RFC.

OAuth 2.0-bakgrund för Windows-appar

Windows Runtime (WinRT) WebAuthenticationBroker, främst utformad för UWP-appar, medför flera utmaningar när den används i skrivbordsappar. Viktiga problem är beroendet av ApplicationView, som inte är kompatibel med skrivbordsappramverk. Därför måste utvecklare tillgripa lösningar som omfattar interop-gränssnitt och ytterligare kod för att implementera OAuth 2.0-funktioner i WinUI 3 och andra skrivbordsappar.

OAuth2Manager-API i Windows App SDK

OAuth2Manager API för Windows App SDK tillhandahåller en strömlinjeformad lösning som uppfyller utvecklarnas förväntningar. Den erbjuder sömlösa OAuth 2.0-funktioner med fullständig funktionsparitet på alla Windows-plattformar som stöds av Windows App SDK. Det nya API:et eliminerar behovet av besvärliga lösningar och förenklar processen med att införliva OAuth 2.0-funktioner i skrivbordsappar.

OAuth2Manager skiljer sig från WebAuthenticationBroker i WinRT. Den följer metodtipsen för OAuth 2.0 närmare – till exempel med hjälp av användarens standardwebbläsare. Metodtipsen för API:et kommer från IETF (Internet Engineering Task Force) OAuth 2.0 Authorization Framework RFC 6749, PKCE RFC 7636 och OAuth 2.0 för Native Apps RFC 8252.

OAuth 2.0-kodexempel

En fullständig WinUI 3-exempelapp finns på GitHub. Följande avsnitt innehåller kodfragment för de vanligaste OAuth 2.0-flödena med hjälp av OAuth2Manager API.

Begäran om auktoriseringskod

I följande exempel visas hur du utför en begäran om auktoriseringskod med hjälp av OAuth2Manager- i Windows App SDK:

// Get the WindowId for the application window
Microsoft::UI::WindowId parentWindowId = this->AppWindow().Id();

AuthRequestParams authRequestParams = AuthRequestParams::CreateForAuthorizationCodeRequest(L"my_client_id",
   Uri(L"my-app:/oauth-callback/"));
authRequestParams.Scope(L"user:email user:birthday");

AuthRequestResult authRequestResult = co_await OAuth2Manager::RequestAuthWithParamsAsync(parentWindowId, 
   Uri(L"https://my.server.com/oauth/authorize"), authRequestParams);
if (AuthResponse authResponse = authRequestResult.Response())
{
   //To obtain the authorization code
   //authResponse.Code();

   //To obtain the access token
   DoTokenExchange(authResponse);
}
else
{
   AuthFailure authFailure = authRequestResult.Failure();
   NotifyFailure(authFailure.Error(), authFailure.ErrorDescription());
}

Exchange-auktoriseringskod för åtkomsttoken

I följande exempel visas hur du utbyter en auktoriseringskod för en åtkomsttoken med hjälp av OAuth2Manager.

För offentliga klienter (t.ex. interna skrivbordsappar) som använder PKCE ska du inte inkludera någon klienthemlighet. PKCE-kodverifieraren tillhandahåller i stället säkerheten:

AuthResponse authResponse = authRequestResult.Response();
TokenRequestParams tokenRequestParams = TokenRequestParams::CreateForAuthorizationCodeRequest(authResponse);

// For public clients using PKCE, do not include ClientAuthentication
TokenRequestResult tokenRequestResult = co_await OAuth2Manager::RequestTokenAsync(
    Uri(L"https://my.server.com/oauth/token"), tokenRequestParams);
if (TokenResponse tokenResponse = tokenRequestResult.Response())
{
    String accessToken = tokenResponse.AccessToken();
    String tokenType = tokenResponse.TokenType();

    // RefreshToken string null/empty when not present
    if (String refreshToken = tokenResponse.RefreshToken(); !refreshToken.empty())
    {
        // ExpiresIn is zero when not present
        DateTime expires = winrt::clock::now();
        if (String expiresIn = tokenResponse.ExpiresIn(); std::stoi(expiresIn) != 0)
        {
            expires += std::chrono::seconds(static_cast<int64_t>(std::stoi(expiresIn)));
        }
        else
        {
            // Assume a duration of one hour
            expires += std::chrono::hours(1);
        }

        //Schedule a refresh of the access token
        myAppState.ScheduleRefreshAt(expires, refreshToken);
    }

    // Use the access token for resources
    DoRequestWithToken(accessToken, tokenType);
}
else
{
    TokenFailure tokenFailure = tokenRequestResult.Failure();
    NotifyFailure(tokenFailure.Error(), tokenFailure.ErrorDescription());
}

För konfidentiella klienter (till exempel webbappar eller tjänster) som har en klienthemlighet inkluderar du parametern ClientAuthentication :

AuthResponse authResponse = authRequestResult.Response();
TokenRequestParams tokenRequestParams = TokenRequestParams::CreateForAuthorizationCodeRequest(authResponse);
ClientAuthentication clientAuth = ClientAuthentication::CreateForBasicAuthorization(L"my_client_id",
    L"my_client_secret");

TokenRequestResult tokenRequestResult = co_await OAuth2Manager::RequestTokenAsync(
    Uri(L"https://my.server.com/oauth/token"), tokenRequestParams, clientAuth);
// Handle the response as shown in the previous example

Uppdatera en åtkomsttoken

I följande exempel visas hur du uppdaterar en åtkomsttoken med hjälp av OAuth2ManagersRefreshTokenAsync-metod .

För offentliga klienter som använder PKCE utelämnar du parametern ClientAuthentication :

TokenRequestParams tokenRequestParams = TokenRequestParams::CreateForRefreshToken(refreshToken);

// For public clients using PKCE, do not include ClientAuthentication
TokenRequestResult tokenRequestResult = co_await OAuth2Manager::RequestTokenAsync(
    Uri(L"https://my.server.com/oauth/token"), tokenRequestParams);
if (TokenResponse tokenResponse = tokenRequestResult.Response())
{
    UpdateToken(tokenResponse.AccessToken(), tokenResponse.TokenType(), tokenResponse.ExpiresIn());

    //Store new refresh token if present
    if (String refreshToken = tokenResponse.RefreshToken(); !refreshToken.empty())
    {
        // ExpiresIn is zero when not present
        DateTime expires = winrt::clock::now();
        if (String expiresInStr = tokenResponse.ExpiresIn(); !expiresInStr.empty())
        {
            int expiresIn = std::stoi(expiresInStr);
            if (expiresIn != 0)
            {
                expires += std::chrono::seconds(static_cast<int64_t>(expiresIn));
            }
        }
        else
        {
            // Assume a duration of one hour
            expires += std::chrono::hours(1);
        }

        //Schedule a refresh of the access token
        myAppState.ScheduleRefreshAt(expires, refreshToken);
    }
}
else
{
    TokenFailure tokenFailure = tokenRequestResult.Failure();
    NotifyFailure(tokenFailure.Error(), tokenFailure.ErrorDescription());
}

För konfidentiella klienter som har en klienthemlighet inkluderar du parametern ClientAuthentication :

TokenRequestParams tokenRequestParams = TokenRequestParams::CreateForRefreshToken(refreshToken);
ClientAuthentication clientAuth = ClientAuthentication::CreateForBasicAuthorization(L"my_client_id",
    L"my_client_secret");
TokenRequestResult tokenRequestResult = co_await OAuth2Manager::RequestTokenAsync(
    Uri(L"https://my.server.com/oauth/token"), tokenRequestParams, clientAuth);
// Handle the response as shown in the previous example

Slutför en auktoriseringsbegäran

För att slutföra en auktoriseringsbegäran från en protokollaktivering bör appen hantera händelsen AppInstance.Activated . Den här händelsen krävs när din app har anpassad omdirigeringslogik. Ett fullständigt exempel finns på GitHub-.

Använd följande kod:

void App::OnActivated(const IActivatedEventArgs& args)
{
    if (args.Kind() == ActivationKind::Protocol)
    {
        auto protocolArgs = args.as<ProtocolActivatedEventArgs>();
        if (OAuth2Manager::CompleteAuthRequest(protocolArgs.Uri()))
        {
            TerminateCurrentProcess();
        }

        DisplayUnhandledMessageToUser();
    }
}