Freigeben über


Implementieren von OAuth 2.0 in Windows Apps

Mit dem OAuth2Manager in Windows App SDK können Desktopanwendungen wie WinUI 3 die OAuth 2.0-Autorisierung für Windows nahtlos ausführen. Die OAuth2Manager-API stellt keine APIs für die impliziten Anforderungs- und Ressourcenbesitzer-Kennwortanmeldeinformationen aufgrund der sicherheitsrelevanten Bedenken bereit. Verwenden Sie den Autorisierungscode-Erteilungstyp mit Proof Key for Code Exchange (PKCE). Weitere Informationen finden Sie unter PKCE RFC.

Hinweis

OAuth2Manager ist für allgemeine OAuth 2.0-Flüsse mit jedem Identitätsanbieter (GitHub, Google, benutzerdefiniert usw.) konzipiert und verwendet immer den Systembrowser für den Autorisierungsschritt. Wenn Sie sich speziell mit Microsoft-Konten oder Microsoft Entra ID Konten (Geschäfts-/Schulkonten) anmelden möchten mit silent SSO – unter Verwendung des bereits bei Windows angemeldeten Kontos, ohne Browseraufforderung – verwenden Sie stattdessen MSAL.NET mit dem Web Account Manager (WAM)-Broker. Web Account Manager bietet auch Windows Hello Integration und Unterstützung für bedingten Zugriff, die OAuth2Manager nicht unterstützt.

OAuth2Manager-API in Windows App SDK

Die OAuth2Manager-API für Windows App SDK bietet eine optimierte Lösung, die den Erwartungen von Entwicklern entspricht. Es bietet nahtlose OAuth 2.0-Funktionen mit vollständiger Funktionsparität auf allen Windows Plattformen, die von Windows App SDK unterstützt werden. Die neue API beseitigt die Notwendigkeit umständlicher Problemumgehungen und vereinfacht den Prozess der Integration von OAuth 2.0-Funktionen in Desktop-Apps.

Der OAuth2Manager unterscheidet sich vom WebAuthenticationBroker in WinRT. Es folgt den bewährten OAuth 2.0-Methoden genauer , z. B. mithilfe des Standardbrowsers des Benutzers. Die bewährten Methoden für die API stammen aus dem IETF (Internet Engineering Task Force) OAuth 2.0 Authorization Framework RFC 6749, PKCE RFC 7636 und OAuth 2.0 für Native Apps RFC 8252.

OAuth 2.0-Codebeispiele

Eine vollständige WinUI-Beispiel-App ist auf GitHub verfügbar. In den folgenden Abschnitten werden Codeausschnitte für die am häufigsten verwendeten OAuth 2.0-Flüsse mithilfe der OAuth2Manager-API bereitgestellt.

Autorisierungscodeanforderung

Im folgenden Beispiel wird veranschaulicht, wie Eine Autorisierungscodeanforderung mithilfe der OAuth2Manager in Windows App SDK ausgeführt wird:

// 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 Autorisierungscode für Zugriffstoken

Das folgende Beispiel zeigt, wie Sie einen Autorisierungscode in ein Access-Token umwandeln, indem Sie den OAuth2Manager verwenden.

Für öffentliche Clients (z. B. native Desktop-Apps), die PKCE verwenden, schließen Sie keinen geheimen Clientschlüssel ein. Die PKCE-Codeüberprüfung stellt stattdessen die Sicherheit bereit:

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());
}

Schließen Sie für confidential clients (z. B. web apps oder Dienste) mit einem geheimen Clientschlüssel den Parameter ClientAuthentication ein:

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

Aktualisieren eines access-Tokens

Das folgende Beispiel zeigt, wie Sie ein access-Token mithilfe der OAuth2ManagerRefreshTokenAsync-Methode aktualisieren.

Für öffentliche Clients , die PKCE verwenden, lassen Sie den ClientAuthentication Parameter aus:

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ügen Sie für vertrauliche Clients mit einem geheimen Clientschlüssel den ClientAuthentication Parameter ein:

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

Abschließen einer Autorisierungsanforderung

Um eine Autorisierungsanforderung über eine Protokollaktivierung abzuschließen, sollte Ihre App das Ereignis "AppInstance.Activated " behandeln. Dieses Ereignis ist erforderlich, wenn Ihre App über eine benutzerdefinierte Umleitungslogik verfügt. Ein vollständiges Beispiel ist für GitHub verfügbar.

Verwenden Sie den folgenden Code:

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

        DisplayUnhandledMessageToUser();
    }
}