Compartilhar via


Implementar o OAuth 2.0 em aplicativos do Windows

O OAuth2Manager no SDK de Aplicativos do Windows permite que aplicativos da área de trabalho, como o WinUI 3, executem perfeitamente a autorização do OAuth 2.0 no Windows. A API OAuth2Manager não fornece APIs para a solicitação implícita e a credencial de senha do proprietário do recurso devido às preocupações de segurança que envolvem. Use o tipo de concessão de código de autorização com Chave de Prova para Troca de Códigos (PKCE). Para mais informações, consulte PKCE RFC.

Informações de fundo do OAuth 2.0 para aplicativos do Windows

O WebAuthenticationBroker do Windows Runtime (WinRT), projetado principalmente para aplicativos UWP, apresenta vários desafios quando usado em aplicativos da área de trabalho. Os principais problemas incluem a dependência de ApplicationView, que não é compatível com estruturas de aplicativos da área de trabalho. Como resultado, os desenvolvedores devem recorrer a soluções alternativas envolvendo interfaces de interoperabilidade e código adicional para implementar a funcionalidade do OAuth 2.0 no WinUI 3 e em outros aplicativos da área de trabalho.

API OAuth2Manager no SDK de Aplicativos do Windows

A API OAuth2Manager para o SDK de Aplicativos do Windows fornece uma solução simplificada que atende às expectativas dos desenvolvedores. Ele oferece funcionalidades contínuas e integradas do OAuth 2.0, com paridade total de recursos em todas as plataformas Windows suportadas pelo Windows App SDK. A nova API elimina a necessidade de soluções alternativas complicadas e simplifica o processo de incorporação da funcionalidade do OAuth 2.0 em aplicativos da área de trabalho.

O OAuth2Manager é diferente do WebAuthenticationBroker no WinRT. Ele segue as práticas recomendadas do OAuth 2.0 mais de perto , por exemplo, usando o navegador padrão do usuário. As práticas recomendadas para a API vêm do IETF (Internet Engineering Task Force) OAuth 2.0 Authorization Framework RFC 6749, PKCE RFC 7636 e OAuth 2.0 for Native Apps RFC 8252.

Exemplos de código OAuth 2.0

Um aplicativo de exemplo completo do WinUI 3 está disponível no GitHub. As seções a seguir fornecem snippets de código para os fluxos OAuth 2.0 mais comuns usando a API OAuth2Manager.

Solicitação de código de autorização

O exemplo a seguir demonstra como executar uma solicitação de código de autorização usando o OAuth2Manager no SDK do Aplicativo 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());
}

Código de autorização do Exchange para token de acesso

O exemplo a seguir mostra como trocar um código de autorização por um token de acesso usando o OAuth2Manager.

Para clientes públicos (como aplicativos nativos de área de trabalho) que usam PKCE, não inclua um segredo do cliente. Em vez disso, o verificador de código PKCE fornece a segurança:

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

Para clientes confidenciais (como aplicativos Web ou serviços) que têm um segredo do cliente, inclua o ClientAuthentication parâmetro:

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

Atualizar um token de acesso

O exemplo a seguir mostra como atualizar um token de acesso usando o método RefreshTokenAsync do OAuth2Manager.

Para clientes públicos que usam PKCE, omita o ClientAuthentication parâmetro:

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

Para clientes confidenciais que têm um segredo do cliente, inclua o ClientAuthentication parâmetro:

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

Concluir uma solicitação de autorização

Para concluir uma solicitação de autorização de uma ativação de protocolo, seu aplicativo deve manipular o evento AppInstance.Activated . Esse evento é necessário quando seu aplicativo tem lógica de redirecionamento personalizada. Um exemplo completo está disponível no GitHub.

Use o seguinte código:

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

        DisplayUnhandledMessageToUser();
    }
}