前提条件
-
Windows アプリ SDK試験段階のリリース チャネル— OAuth2Manager は、安定したチャネルでは使用できません。
Microsoft.WindowsAppSDKサフィックスを持つプレリリース-experimentalバージョンをインストールします ([NuGet にプレリリースを含める] を選択します)。 詳細については、「 開発環境のセットアップ」を参照してください。
概要
Windows アプリ SDKの OAuth2Manager を使用すると、WinUI 3 などのデスクトップ アプリケーションで、Windowsで OAuth 2.0 承認を実行できます。 OAuth2Manager API では、セキュリティ上の問題が伴うため、暗黙的な要求とリソース所有者のパスワード資格情報の API は提供されません。 認可コード付与タイプとコードエクスチェンジのための証明鍵(PKCE)を使用します。 詳細については、 PKCE RFC を参照してください。
注
OAuth2Manager は、任意の ID プロバイダー (GitHub、Google、カスタムなど) との一般的な OAuth 2.0 フロー用に設計されており、承認手順には常にシステム ブラウザーを使用します。
Windows アプリ SDK の OAuth2Manager API
Windows アプリ SDK用 OAuth2Manager API は、デスクトップ アプリ用の PKCE を使用して OAuth 2.0 承認コード フローを処理します。 Windows アプリ SDKでサポートされているすべてのWindowsプラットフォームで動作し、サードパーティのブラウザーベースの回避策を必要とせずに一貫したトークン取得インターフェイスを提供します。
OAuth2Manager は、WinRT の WebAuthenticationBroker とは異なります。 ユーザーの既定のブラウザーを使用するなど、OAuth 2.0 のベスト プラクティスに従います。 この API のベスト プラクティスは、IETF (インターネット エンジニアリング タスク フォース) の OAuth 2.0 Authorization Framework RFC 6749、PKCE RFC 7636、および OAuth 2.0 for Native Apps RFC 8252 に由来します。
OAuth 2.0 のコード例
完全な WinUI サンプル アプリは、GitHub で入手できます。 次のセクションでは、 OAuth2Manager API を使用する最も一般的な OAuth 2.0 フローのコード スニペットを示します。
承認コード要求
次の例では、Windows アプリ SDK で OAuth2Manager を使用して認証コード要求を実行する方法を示します。
// 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());
}
承認コードをアクセス トークンに交換する
次の例では、OAuth2Manager を使用してaccess トークンの承認コードをexchangeする方法を示します。
PKCE を使用する パブリック クライアント (ネイティブ デスクトップ アプリなど) の場合は、クライアント シークレットを含めないでください。 PKCE コード検証ツールは、代わりに次のセキュリティを提供します。
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());
}
Important
デスクトップ アプリはパブリック クライアントであり、クライアント シークレットを埋め込む必要はありません。 次の機密クライアント パターンは、資格情報を安全に格納できるサーバー側コンポーネント (Web アプリ、API、バックエンド サービス) にのみ適用されます。 ネイティブ デスクトップ アプリでは、クライアント シークレットを抽出から保護できません。代わりに、上記の PKCE でパブリック クライアント パターンを使用します。 アーキテクチャでトークン交換にクライアント シークレットが必要な場合は、デスクトップ アプリではなくバックエンド サービスでその交換を実行します。
クライアント シークレットを持つクライアント (web appsやサービスなど) の場合は、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
access トークンを更新する
次の例は、OAuth2Manager の RefreshTokenAsync メソッドを使用してaccess トークンを更新する方法を示しています。
PKCE を使用する パブリック クライアント の場合は、 ClientAuthentication パラメーターを省略します。
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());
}
クライアント シークレットを持つ 機密クライアント の場合は、 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
承認要求を完了する
プロトコルのアクティブ化からの承認要求を完了するには、アプリで AppInstance.Activated イベントを処理する必要があります。 このイベントは、アプリにカスタム リダイレクト ロジックがある場合に必要です。 完全な例は、GitHub で使用できます。
次のコードを使用します。
void App::OnActivated(const IActivatedEventArgs& args)
{
if (args.Kind() == ActivationKind::Protocol)
{
auto protocolArgs = args.as<ProtocolActivatedEventArgs>();
if (OAuth2Manager::CompleteAuthRequest(protocolArgs.Uri()))
{
TerminateCurrentProcess();
}
DisplayUnhandledMessageToUser();
}
}
関連コンテンツ
Windows developer