Windows App SDK の OAuth2Manager を使用すると、WinUI 3 などのデスクトップ アプリケーションで Windows で OAuth 2.0 承認をシームレスに実行できます。 OAuth2Manager API では、セキュリティ上の問題が伴うため、暗黙的な要求とリソース所有者のパスワード資格情報の API は提供されません。 認証コード許可の種類とコード交換用証明キー (PKCE) を使用します。 詳細については、 PKCE RFC を参照してください。
Windows アプリの OAuth 2.0 の背景
Windows ランタイム (WinRT) WebAuthenticationBroker は、主に UWP アプリ用に設計されており、デスクトップ アプリで使用する場合、いくつかの課題を提示します。 主な問題には、デスクトップ アプリ フレームワークと互換性のない ApplicationView への依存関係が含まれます。 その結果、開発者は、WinUI 3 やその他のデスクトップ アプリに OAuth 2.0 機能を実装するために、相互運用インターフェイスと追加のコードを含む回避策に頼る必要があります。
Windows App SDK の OAuth2Manager API
Windows App SDK 用 OAuth2Manager API は、開発者の期待に応える合理化されたソリューションを提供します。 Windows App SDK でサポートされているすべての Windows プラットフォームで完全な機能パリティを備えたシームレスな OAuth 2.0 機能を提供します。 新しい API を使用すると、面倒な回避策が不要になり、OAuth 2.0 機能をデスクトップ アプリに組み込むプロセスが簡略化されます。
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 3 サンプル アプリは GitHub で入手できます。 次のセクションでは、 OAuth2Manager API を使用する最も一般的な OAuth 2.0 フローのコード スニペットを示します。
承認コード要求
次の例では、Windows App 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());
}
アクセス トークンの Exchange 承認コード
次の例は、 OAuth2Manager を使用してアクセス トークンの承認コードを交換する方法を示しています。
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());
}
クライアント シークレットを持つ 機密クライアント (Web アプリやサービスなど) の場合は、 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
アクセス トークンを更新する
次の例は、 OAuth2Manager の RefreshTokenAsync メソッドを使用してアクセス トークンを更新する方法を示しています。
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