Bagikan melalui


Menerapkan OAuth 2.0 di aplikasi Windows

OAuth2Manager di Windows App SDK memungkinkan aplikasi desktop seperti WinUI 3 melakukan otorisasi OAuth 2.0 dengan mulus di Windows. API OAuth2Manager tidak menyediakan API untuk permintaan implisit dan kredensial kata sandi pemilik sumber daya karena masalah keamanan yang terjadi. Gunakan jenis pemberian kode otorisasi dengan Proof Key for Code Exchange (PKCE). Untuk informasi selengkapnya, lihat PKCE RFC.

Latar belakang OAuth 2.0 untuk aplikasi Windows

Windows Runtime (WinRT) WebAuthenticationBroker, terutama dirancang untuk aplikasi UWP, menghadirkan beberapa tantangan saat digunakan di aplikasi desktop. Masalah utama termasuk dependensi pada ApplicationView, yang tidak kompatibel dengan kerangka kerja aplikasi desktop. Akibatnya, pengembang harus menggunakan solusi yang melibatkan antarmuka interop dan kode tambahan untuk menerapkan fungsionalitas OAuth 2.0 ke WinUI dan aplikasi desktop lainnya.

API OAuth2Manager di Windows App SDK

API OAuth2Manager untuk Windows App SDK menyediakan solusi yang disederhanakan yang memenuhi harapan pengembang. Ini menawarkan kemampuan OAuth 2.0 yang mulus dengan paritas fitur lengkap di semua platform Windows yang didukung oleh Windows App SDK. API baru menghilangkan kebutuhan akan solusi yang rumit dan menyederhanakan proses penggabungan fungsionalitas OAuth 2.0 ke dalam aplikasi desktop.

OAuth2Manager berbeda dari WebAuthenticationBroker di WinRT. Ini mengikuti praktik terbaik OAuth 2.0 lebih tepat - misalnya, dengan menggunakan peramban bawaan pengguna. Praktik terbaik untuk API berasal dari IETF (Internet Engineering Task Force) OAuth 2.0 Authorization Framework RFC 6749, PKCE RFC 7636, dan OAuth 2.0 untuk Native Apps RFC 8252.

Contoh kode OAuth 2.0

Aplikasi sampel WinUI lengkap tersedia di GitHub. Bagian berikut menyediakan cuplikan kode untuk alur OAuth 2.0 yang paling umum menggunakan OAuth2Manager API.

Permintaan kode otorisasi

Contoh berikut menunjukkan cara melakukan permintaan kode otorisasi menggunakan OAuth2Manager di 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());
}

tukar kode otorisasi untuk token akses

Contoh berikut menunjukkan cara exchange kode otorisasi untuk token access dengan menggunakan OAuth2Manager.

Untuk klien publik (seperti aplikasi desktop asli) yang menggunakan PKCE, jangan sertakan rahasia klien. Pemverifikasi kode PKCE menyediakan keamanan sebagai gantinya:

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

Untuk klien konfidensial (seperti aplikasi web atau layanan) yang memiliki kunci rahasia klien, sertakan parameter 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

Menyegarkan token akses

Contoh berikut menunjukkan cara me-refresh token access dengan menggunakan metode OAuth2ManagerRefreshTokenAsync.

Untuk klien publik yang menggunakan PKCE, hilangkan ClientAuthentication parameter:

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

Untuk klien rahasia yang memiliki rahasia klien, sertakan ClientAuthentication parameter :

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

Menyelesaikan permintaan otorisasi

Untuk menyelesaikan permintaan otorisasi dari aktivasi protokol, aplikasi Anda harus menangani peristiwa AppInstance.Activated . Kejadian ini diperlukan saat aplikasi Anda memiliki logika pengalihan kustom. Contoh lengkap tersedia pada GitHub.

Gunakan kode berikut:

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

        DisplayUnhandledMessageToUser();
    }
}