Partilhar via


Implementar o OAuth 2.0 nas aplicações Windows

O OAuth2Manager em SDK de Aplicações Windows permite que aplicações de ambiente de trabalho como o WinUI 3 executem a autorização OAuth 2.0 de forma fluida 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 isso implica. Use o tipo de concessão de código de autorização com Proof Key for Code Exchange (PKCE). Para obter mais informações, consulte a RFC PKCE.

Observação

OAuth2Manager foi concebido para fluxos gerais de OAuth 2.0 com qualquer fornecedor de identidade (GitHub, Google, personalizado, etc.) e utiliza sempre o navegador do sistema para a etapa de autorização. Se quiser iniciar sessão especificamente com contas Microsoft ou contas Microsoft Entra ID (trabalho/escola) com SSO silencioso — usando a conta já iniciada sessão no Windows, sem aviso do navegador — use MSAL.NET com o intermediário do Gestor de Contas Web (WAM) em vez disso. O Web Account Manager também oferece integração com o Windows Hello e suporte para acesso condicional que o OAuth2Manager não oferece.

OAuth2Manager API no SDK de Aplicações Windows

A API OAuth2Manager para SDK de Aplicações Windows fornece uma solução simplificada que satisfaz as expectativas dos programadores. Oferece capacidades OAuth 2.0 sem falhas com paridade total de funcionalidades em todas as plataformas Windows suportadas pelo SDK de Aplicações Windows. A nova API elimina a necessidade de soluções alternativas complicadas e simplifica o processo de incorporação da funcionalidade OAuth 2.0 em aplicativos de desktop.

O OAuth2Manager é diferente do WebAuthenticationBroker no WinRT. Ele segue as melhores práticas 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

Uma aplicação de exemplo completa do WinUI está disponível em GitHub. As seções a seguir fornecem trechos de código para os fluxos OAuth 2.0 mais comuns usando o OAuth2Manager API.

Pedido de código de autorização

O exemplo seguinte demonstra como realizar um pedido de código de autorização usando o OAuth2Manager em SDK de Aplicações 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 Exchange para token de acesso

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

Para clientes públicos (como aplicativos de área de trabalho nativos) 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 web apps ou serviços) que tenham um cliente secreto, incluam o parâmetro 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

Atualizar um token de acesso

O exemplo seguinte mostra como atualizar um token access 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 de 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

Preencher um pedido 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 em 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();
    }
}