Udostępnij za pośrednictwem


Implementowanie protokołu OAuth 2.0 w aplikacjach Windows

OAuth2Manager w Zestaw SDK do aplikacji systemu Windows umożliwia aplikacjom komputerowym, takim jak WinUI 3 bezproblemowe wykonywanie autoryzacji OAuth 2.0 na Windows. API OAuth2Manager nie udostępnia API dla implicitnych żądań i poświadczeń hasła właściciela zasobu ze względu na względy bezpieczeństwa. Użyj typu przyznania kodu autoryzacji z dowodem klucza wymiany kodu (PKCE). Aby uzyskać więcej informacji, zobacz PKCE RFC.

Uwaga / Notatka

OAuth2Manager jest przeznaczony dla ogólnych przepływów OAuth 2.0 z dowolnym dostawcą tożsamości (GitHub, Google, niestandardowy itp.) i zawsze używa przeglądarki systemowej do kroku autoryzacji. Jeśli chcesz zalogować się korzystając z kont Microsoft albo kont Microsoft Entra ID (służbowych/szkolnych) przy użyciu silent SSO — używając konta, które zostało już zalogowane w systemie Windows, bez monitu przeglądarki — zamiast tego użyj MSAL.NET przy użyciu brokera Menedżera Kont Sieci Web. Menedżer kont sieci Web zapewnia również integrację Windows Hello i obsługę dostępu warunkowego, których nie obsługuje protokół OAuth2Manager.

Interfejs API OAuth2Manager w Zestaw SDK do aplikacji systemu Windows

Interfejs API OAuth2Manager dla Zestaw SDK do aplikacji systemu Windows zapewnia usprawnione rozwiązanie spełniające oczekiwania deweloperów. Oferuje ona bezproblemowe funkcje protokołu OAuth 2.0 z pełną parzystością funkcji na wszystkich platformach Windows obsługiwanych przez Zestaw SDK do aplikacji systemu Windows. Nowy interfejs API eliminuje potrzebę kłopotliwych obejść i upraszcza proces dołączania funkcji OAuth 2.0 do aplikacji klasycznych.

Element OAuth2Manager różni się od elementu WebAuthenticationBroker w środowisku WinRT. Jest to bardziej zgodne z najlepszymi rozwiązaniami protokołu OAuth 2.0 — na przykład przy użyciu domyślnej przeglądarki użytkownika. Najlepsze rozwiązania dotyczące interfejsu API pochodzą z IETF (Internet Engineering Task Force) OAuth 2.0 Authorization Framework RFC 6749, PKCE RFC 7636 i OAuth 2.0 for Native Apps RFC 8252.

Przykłady kodu OAuth 2.0

Pełna przykładowa aplikacja WinUI jest dostępna w GitHub. Poniższe sekcje zawierają fragmenty kodu dla najbardziej typowych przepływów protokołu OAuth 2.0 przy użyciu interfejsu API OAuth2Manager.

Żądanie kodu autoryzacji

W poniższym przykładzie pokazano, jak wykonać żądanie kodu autoryzacji przy użyciu OAuth2Manager w Zestaw SDK do aplikacji systemu Windows:

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

kod autoryzacji Exchange tokenu dostępu

W poniższym przykładzie pokazano, jak wymienić kod autoryzacji na token dostępu przy użyciu OAuth2Manager.

W przypadku klientów publicznych, takich jak natywne aplikacje klasyczne, korzystających z PKCE, nie dołączaj tajnego hasła klienta. Zamiast tego, weryfikator kodu PKCE zapewnia zabezpieczenia.

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

W przypadku klientów poufnych (takich jak aplikacje internetowe lub usługi), które mają tajny klucz klienta, dołącz parametr 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

Odśwież token dostępu

W poniższym przykładzie pokazano, jak odświeżyć token dostępu przy użyciu metody OAuth2Manager's RefreshTokenAsync.

W przypadku klientów publicznych korzystających z PKCE pomiń ClientAuthentication parametr :

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

W przypadku poufnych klientów, którzy mają tajny klucz klienta, dołącz parametr 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

Ukończ żądanie autoryzacji

Aby ukończyć żądanie autoryzacji z aktywacji protokołu, aplikacja powinna obsługiwać zdarzenie AppInstance.Activated . To zdarzenie jest wymagane, gdy aplikacja ma niestandardową logikę przekierowania. Pełny przykład jest dostępny w GitHub.

Użyj następującego kodu:

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

        DisplayUnhandledMessageToUser();
    }
}