Внешняя аутентификация Microsoft Graph для мобильного приложения с ASP.NET WebApi 2
Я создаю мобильное приложение (Xamarin + MvvmCross), которое обращается к моим сервисам через Web API 2 + Owin и пытаюсь поддерживать внешний вход в систему. Предположим, что я уже зарегистрировал пользователя с помощью внешней аутентификации и просто хочу войти в систему с помощью мобильного приложения.
В настоящее время у меня есть логин на Facebook, работающий в основном после внешних входов в ASP.NET Web API 2 через Facebook и Google в приложении AngularJS, однако спустя много часов я не могу понять, как реализовать ту же стратегию для входов в Microsoft, как и я. найти способ проверить токен доступа Microsoft.
Насколько я понимаю, для вызова моих собственных API (например, для получения моих пользовательских данных и объектов) мне нужно обменять токен внешнего доступа на токен локального доступа из моей собственной службы, выполнив сначала следующее:
- Аутентификация с внешним провайдером (в мобильном приложении)
- Отправить токен доступа провайдеров на мой сервер (Web Api 2)
- Проверьте токен доступа у провайдера, чтобы определить, является ли пользователь легальным - в процессе получения ProviderKey (userId в Facebook и Google)
- Войдите в систему локально, используя имя поставщика и идентификатор пользователя, начиная с шага 3
- Создайте локальный токен доступа для текущего пользователя.
В настоящее время Facebook проходит через весь процесс, указанный выше, в Интернете и в мобильном приложении. С помощью Microsoft я могу проходить аутентификацию в мобильном приложении и получать токен доступа Microsoft, однако я застрял на шаге 3, проверяя токен доступа на моем сервере, чтобы получить любую полезную информацию, которую я могу использовать для идентификации уже зарегистрированного пользователя в моем приложении.
Чтобы определить правильного пользователя, мне нужны провайдер входа и ProviderKey из таблицы AspNetUserLogins.
После получения токена Microsoft Access, как я могу проверить токен и проверить его на соответствие имен и ключей провайдера?
Это общий / стандартный подход для внешней аутентификации? Я думал, что было бы гораздо больше информации об этом, если бы это было так. Все и любая помощь с благодарностью.
Дополнительная информация
Я также пытаюсь поддерживать как обычные учетные записи Microsoft, так и учетные записи Work/School, используя новый Microsoft Graph Api (унифицированный office365). Таким образом, кажется, что существуют разные ключи провайдера в зависимости от учетной записи Microsoft/ организации, например https://login.microsoftonline.com/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/v2.0
Примеры кода
Шаг 3 - Проверка маркера внешнего доступа для получения информации для входа пользователя
Private async Task<ParsedExternalAccessToken> VerifyExternalAccessToken(string provider, string accessToken) {
ParsedExternalAccessToken parsedToken = null;
var verifyTokenEndPoint = "";
if (provider == Resources.Constants.FacebookProvider) {
//You can get it from here: https://developers.facebook.com/tools/accesstoken/
//More about debug_tokn here: https://stackru.com/questions/16641083/how-does-one-get-the-app-access-token-for-debug-token-inspection-on-facebook
var appToken = WebConfigurationManager.AppSettings["fb_app_token"];
verifyTokenEndPoint = string.Format("https://graph.facebook.com/debug_token?input_token={0}&access_token={1}", accessToken, appToken);
} else if (provider == Resources.Constants.GoogleProvider) {
verifyTokenEndPoint = string.Format("https://www.googleapis.com/oauth2/v1/tokeninfo?access_token={0}", accessToken);
} else if (provider == "Microsoft" || provider == Resources.Constants.MicrosoftAccountProvider || provider == Resources.Constants.MicrooftSchoolOrWorkAccountProvider) {
//made up end point -> what is the real answer/solution?
verifyTokenEndPoint = string.Format("https://login.microsoftonline.com/common/v2.0/tokeninfo?access_token={0}", accessToken);
} else {
return null;
}
var client = new HttpClient();
var uri = new Uri(verifyTokenEndPoint);
var response = await client.GetAsync(uri);
if (response.IsSuccessStatusCode) {
var content = await response.Content.ReadAsStringAsync();
dynamic jObj = (JObject)Newtonsoft.Json.JsonConvert.DeserializeObject(content);
parsedToken = new ParsedExternalAccessToken();
if (provider == Resources.Constants.FacebookProvider) {
parsedToken.user_id = jObj["data"]["user_id"];
parsedToken.app_id = jObj["data"]["app_id"];
if (!string.Equals(Startup.facebookAuthOptions.AppId, parsedToken.app_id, StringComparison.OrdinalIgnoreCase)) {
return null;
}
} else if (provider == Resources.Constants.GoogleProvider) {
parsedToken.user_id = jObj["user_id"];
parsedToken.app_id = jObj["audience"];
if (!string.Equals(Startup.googleAuthOptions.ClientId, parsedToken.app_id, StringComparison.OrdinalIgnoreCase)) {
return null;
}
} else if (provider == Resources.Constants.MicrosoftAccountProvider || provider == Resources.Constants.MicrooftSchoolOrWorkAccountProvider) {
throw new NotImplementedException("Microsoft Access Token Validation not implemented");
}
}
return parsedToken;
}
Шаг 4 - Получить токен локального доступа
public async Task<IHttpActionResult> ObtainLocalAccessToken(string provider, string externalAccessToken) {
if (string.IsNullOrWhiteSpace(provider) || string.IsNullOrWhiteSpace(externalAccessToken)) {
return ApiErrorResult(ServiceResults.ExternalAuth.Codes.ProviderOrExternalAccessTokenIsNotSent, ServiceResults.ExternalAuth.Messages.ProviderOrExternalAccessTokenIsNotSent);
}
var verifiedAccessToken = await VerifyExternalAccessToken(provider, externalAccessToken);
if (verifiedAccessToken == null) {
return ApiErrorResult(ServiceResults.ExternalAuth.Codes.InvalidProviderOrExternalAccessToken, ServiceResults.ExternalAuth.Messages.InvalidProviderOrExternalAccessToken);
}
var user = await _userManager.FindAsync(new UserLoginInfo(provider, verifiedAccessToken.user_id));
bool hasRegistered = user != null;
if (!hasRegistered) {
return ApiErrorResult(ServiceResults.ExternalAuth.Codes.ExternalUserIsNotRegistered, ServiceResults.ExternalAuth.Messages.ExternalUserIsNotRegistered);
}
//generate access token response
var accessTokenResponse = await GenerateLocalOauthToken(user);
return Ok(accessTokenResponse);
}
Спасибо
ОБНОВИТЬ
Я попытался реализовать оба варианта @dstrockis с ограниченным успехом. В обоих вариантах я до сих пор не могу понять, что использовать для соответствия с ProviderKey
При выполнении вызова API Api для графа GET User никакие значения не соответствуют сохраненному ProviderKey, такому как userId, используемый в Facebook и Google. Значение Microsoft ProviderKey выглядит следующим образом: AAAAAAAAAAAAAAAAAAAAADWmHuzvvAQpO*******9PM.
Мне также удалось проверить токен доступа с помощью библиотек проверки JWT, как это было предложено. Просматривая заявки, я могу определить правильный LoginProvider сейчас (хорошее начало), но я все еще не могу найти какие-либо значения, которые соответствуют тому, что хранится в ProviderKey.
Это значение должно иметь отношение к пользователю, но ничего не соответствует напрямую. Кто-нибудь знает, откуда взялся ProviderKey с OpenIdAuthentication для Microsoft. Это зашифровано от другого значения?
Проверка токена JWT
private async Task ValidateMicrosoftToken(string token) {
string stsDiscoveryEndpoint = "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration";
ConfigurationManager<OpenIdConnectConfiguration> configManager = new ConfigurationManager<OpenIdConnectConfiguration>(stsDiscoveryEndpoint);
OpenIdConnectConfiguration config = await configManager.GetConfigurationAsync();
TokenValidationParameters validationParameters = new TokenValidationParameters {
ValidateAudience = false,
ValidateIssuer = false,
IssuerSigningTokens = config.SigningTokens,
ValidateLifetime = false
};
JwtSecurityTokenHandler tokendHandler = new JwtSecurityTokenHandler();
SecurityToken jwt;
var result = tokendHandler.ValidateToken(token, validationParameters, out jwt);
//result contains claims with lots of values
//get provider key from claims????
}
Auth Provider - Startup.Auth
microsoftAccountAuthOptions = new OpenIdConnectAuthenticationOptions() {
Description = new AuthenticationDescription() { AuthenticationType = "OpenIdConnect", Caption = "Microsoft"},
AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Passive,
ClientId = appId,
Authority = authority,
Scope = "openid user.read email " + string.Join(" ", scopes),
RedirectUri = redirectUri,
//PostLogoutRedirectUri = "/",
TokenValidationParameters = new TokenValidationParameters {
//.....
},
Notifications = new OpenIdConnectAuthenticationNotifications {
//.....
}
};
app.UseOpenIdConnectAuthentication(microsoftAccountAuthOptions);
1 ответ
Этот подход прекрасно подходит. Для шага 3 у вас действительно есть два варианта:
Проверьте токен в своем приложении с помощью библиотеки проверки токена. Microsoft Id_tokens - это Json Web Tokens или JWT. Существует несколько библиотек проверки JWT, таких как эта: https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet. Эти библиотеки обычно требуют, чтобы вы выбрали метаданные OpenID Connect (включая ключи подписи токена) и передали их в библиотеку для выполнения проверки. Здесь есть дополнительная информация: https://azure.microsoft.com/en-us/documentation/articles/active-directory-v2-tokens/. Я обычно рекомендую такой подход, поскольку он экономит вам дополнительный вызов API.
Другой подход заключается в том, чтобы следовать шаблонам, которые используют Google и Facebook, и вызывать какой-то веб-API для проверки токена для вас и возврата некоторой информации о пользователе, например, идентификатора пользователя. С Microsoft Graph я бы рекомендовал использовать этот API: https://graph.microsoft.io/en-us/docs/api-reference/v1.0/api/user_get. Обязательно используйте правильные области в своих запросах на аутентификацию.
Удачи!