#489 Update MicrosoftAccount provider
This commit is contained in:
Родитель
06b7b5c236
Коммит
89e3cc5b58
|
@ -6,5 +6,9 @@ namespace Microsoft.Owin.Security.MicrosoftAccount
|
|||
internal static class Constants
|
||||
{
|
||||
internal const string DefaultAuthenticationType = "Microsoft";
|
||||
|
||||
internal const string AuthorizationEndpoint = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize";
|
||||
internal const string TokenEndpoint = "https://login.microsoftonline.com/common/oauth2/v2.0/token";
|
||||
internal const string UserInformationEndpoint = "https://graph.microsoft.com/v1.0/me";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Owin.Infrastructure;
|
||||
|
@ -15,9 +16,6 @@ namespace Microsoft.Owin.Security.MicrosoftAccount
|
|||
{
|
||||
internal class MicrosoftAccountAuthenticationHandler : AuthenticationHandler<MicrosoftAccountAuthenticationOptions>
|
||||
{
|
||||
private const string TokenEndpoint = "https://login.live.com/oauth20_token.srf";
|
||||
private const string GraphApiEndpoint = "https://apis.live.net/v5.0/me";
|
||||
|
||||
private readonly ILogger _logger;
|
||||
private readonly HttpClient _httpClient;
|
||||
|
||||
|
@ -79,7 +77,7 @@ namespace Microsoft.Owin.Security.MicrosoftAccount
|
|||
|
||||
var requestContent = new FormUrlEncodedContent(tokenRequestParameters);
|
||||
|
||||
HttpResponseMessage response = await _httpClient.PostAsync(TokenEndpoint, requestContent, Request.CallCancelled);
|
||||
HttpResponseMessage response = await _httpClient.PostAsync(Options.TokenEndpoint, requestContent, Request.CallCancelled);
|
||||
response.EnsureSuccessStatusCode();
|
||||
string oauthTokenResponse = await response.Content.ReadAsStringAsync();
|
||||
|
||||
|
@ -97,9 +95,11 @@ namespace Microsoft.Owin.Security.MicrosoftAccount
|
|||
return new AuthenticationTicket(null, properties);
|
||||
}
|
||||
|
||||
HttpResponseMessage graphResponse = await _httpClient.GetAsync(
|
||||
GraphApiEndpoint + "?access_token=" + Uri.EscapeDataString(accessToken), Request.CallCancelled);
|
||||
var graphRequest = new HttpRequestMessage(HttpMethod.Get, Options.UserInformationEndpoint);
|
||||
graphRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
|
||||
var graphResponse = await _httpClient.SendAsync(graphRequest, Request.CallCancelled);
|
||||
graphResponse.EnsureSuccessStatusCode();
|
||||
|
||||
string accountString = await graphResponse.Content.ReadAsStringAsync();
|
||||
JObject accountInformation = JObject.Parse(accountString);
|
||||
|
||||
|
@ -165,13 +165,13 @@ namespace Microsoft.Owin.Security.MicrosoftAccount
|
|||
// LiveID requires a scope string, so if the user didn't set one we go for the least possible.
|
||||
if (string.IsNullOrWhiteSpace(scope))
|
||||
{
|
||||
scope = "wl.basic";
|
||||
scope = "https://graph.microsoft.com/user.read";
|
||||
}
|
||||
|
||||
string state = Options.StateDataFormat.Protect(extra);
|
||||
|
||||
string authorizationEndpoint =
|
||||
"https://login.live.com/oauth20_authorize.srf" +
|
||||
Options.AuthorizationEndpoint +
|
||||
"?client_id=" + Uri.EscapeDataString(Options.ClientId) +
|
||||
"&scope=" + Uri.EscapeDataString(scope) +
|
||||
"&response_type=code" +
|
||||
|
|
|
@ -27,6 +27,10 @@ namespace Microsoft.Owin.Security.MicrosoftAccount
|
|||
Scope = new List<string>();
|
||||
BackchannelTimeout = TimeSpan.FromSeconds(60);
|
||||
CookieManager = new CookieManager();
|
||||
|
||||
AuthorizationEndpoint = Constants.AuthorizationEndpoint;
|
||||
TokenEndpoint = Constants.TokenEndpoint;
|
||||
UserInformationEndpoint = Constants.UserInformationEndpoint;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -62,6 +66,21 @@ namespace Microsoft.Owin.Security.MicrosoftAccount
|
|||
/// </summary>
|
||||
public string ClientSecret { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the URI where the client will be redirected to authenticate.
|
||||
/// </summary>
|
||||
public string AuthorizationEndpoint { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the URI the middleware will access to exchange the OAuth token.
|
||||
/// </summary>
|
||||
public string TokenEndpoint { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the URI the middleware will access to obtain the user information.
|
||||
/// </summary>
|
||||
public string UserInformationEndpoint { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets timeout value in milliseconds for back channel communications with Microsoft.
|
||||
/// </summary>
|
||||
|
|
|
@ -52,17 +52,13 @@ namespace Microsoft.Owin.Security.MicrosoftAccount
|
|||
}
|
||||
|
||||
Id = userId.ToString();
|
||||
Name = PropertyValueIfExists("name", userAsDictionary);
|
||||
FirstName = PropertyValueIfExists("first_name", userAsDictionary);
|
||||
LastName = PropertyValueIfExists("last_name", userAsDictionary);
|
||||
|
||||
if (userAsDictionary.ContainsKey("emails"))
|
||||
Name = PropertyValueIfExists("displayName", userAsDictionary);
|
||||
FirstName = PropertyValueIfExists("givenName", userAsDictionary);
|
||||
LastName = PropertyValueIfExists("surname", userAsDictionary);
|
||||
Email = PropertyValueIfExists("mail", userAsDictionary);
|
||||
if (Email == null)
|
||||
{
|
||||
JToken emailsNode = user["emails"];
|
||||
foreach (var childAsProperty in emailsNode.OfType<JProperty>().Where(childAsProperty => childAsProperty.Name == "preferred"))
|
||||
{
|
||||
Email = childAsProperty.Value.ToString();
|
||||
}
|
||||
Email = PropertyValueIfExists("userPrincipalName", userAsDictionary);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,17 +68,13 @@ namespace Microsoft.Owin.Security.MicrosoftAccount
|
|||
public JObject User { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the access token provided by the Microsoft authenication service
|
||||
/// Gets the access token provided by the Microsoft authentication service
|
||||
/// </summary>
|
||||
public string AccessToken { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the refresh token provided by Microsoft authentication service
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Refresh token is only available when wl.offline_access is request.
|
||||
/// Otherwise, it is null.
|
||||
/// </remarks>
|
||||
public string RefreshToken { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -46,12 +46,12 @@ namespace FunctionalTests.Facts.Security.MicrosoftAccount
|
|||
|
||||
// Unauthenticated request - verify Redirect url
|
||||
var response = await httpClient.GetAsync(applicationUrl);
|
||||
Assert.Equal<string>("https://login.live.com/oauth20_authorize.srf", response.Headers.Location.AbsoluteUri.Replace(response.Headers.Location.Query, string.Empty));
|
||||
Assert.Equal<string>("https://login.microsoftonline.com/common/oauth2/v2.0/authorize", response.Headers.Location.AbsoluteUri.Replace(response.Headers.Location.Query, string.Empty));
|
||||
var queryItems = response.Headers.Location.ParseQueryString();
|
||||
Assert.Equal<string>("code", queryItems["response_type"]);
|
||||
Assert.Equal<string>("000000004C0F442C", queryItems["client_id"]);
|
||||
Assert.Equal<string>(applicationUrl + "signin-microsoft", queryItems["redirect_uri"]);
|
||||
Assert.Equal<string>("wl.basic wl.signin", queryItems["scope"]);
|
||||
Assert.Equal<string>("https://graph.microsoft.com/user.read", queryItems["scope"]);
|
||||
Assert.Equal<string>("ValidStateData", queryItems["state"]);
|
||||
Assert.Equal<string>("custom", queryItems["custom_redirect_uri"]);
|
||||
|
||||
|
@ -173,9 +173,6 @@ namespace FunctionalTests.Facts.Security.MicrosoftAccount
|
|||
StateDataFormat = new CustomStateDataFormat(),
|
||||
};
|
||||
|
||||
option.Scope.Add("wl.basic");
|
||||
option.Scope.Add("wl.signin");
|
||||
|
||||
app.UseMicrosoftAccountAuthentication(option);
|
||||
app.UseExternalApplication("Microsoft");
|
||||
}
|
||||
|
@ -185,9 +182,12 @@ namespace FunctionalTests.Facts.Security.MicrosoftAccount
|
|||
{
|
||||
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
var response = new HttpResponseMessage();
|
||||
var response = new HttpResponseMessage()
|
||||
{
|
||||
Content = new StringContent("")
|
||||
};
|
||||
|
||||
if (request.RequestUri.AbsoluteUri.StartsWith("https://login.live.com/oauth20_token.srf"))
|
||||
if (request.RequestUri.AbsoluteUri.StartsWith("https://login.microsoftonline.com/common/oauth2/v2.0/token"))
|
||||
{
|
||||
var formData = request.Content.ReadAsFormDataAsync().Result;
|
||||
|
||||
|
@ -198,7 +198,7 @@ namespace FunctionalTests.Facts.Security.MicrosoftAccount
|
|||
if (formData["redirect_uri"] != null && formData["redirect_uri"].EndsWith("signin-microsoft") &&
|
||||
formData["client_id"] == "000000004C0F442C" && formData["client_secret"] == "EkXbW-Vr6Rqzi6pugl1jWIBsDotKLmqR")
|
||||
{
|
||||
response.Content = new StringContent("{\"token_type\":\"bearer\",\"expires_in\":3600,\"scope\":\"wl.basic\",\"access_token\":\"ValidAccessToken\",\"refresh_token\":\"ValidRefreshToken\",\"authentication_token\":\"ValidAuthenticationToken\"}");
|
||||
response.Content = new StringContent("{\"token_type\":\"bearer\",\"expires_in\":3600,\"scope\":\"https://graph.microsoft.com/user.read\",\"access_token\":\"ValidAccessToken\",\"refresh_token\":\"ValidRefreshToken\",\"authentication_token\":\"ValidAuthenticationToken\"}");
|
||||
}
|
||||
}
|
||||
else if (formData["code"] == "InvalidCert")
|
||||
|
@ -231,12 +231,11 @@ namespace FunctionalTests.Facts.Security.MicrosoftAccount
|
|||
}
|
||||
}
|
||||
}
|
||||
else if (request.RequestUri.AbsoluteUri.StartsWith("https://apis.live.net/v5.0/me"))
|
||||
else if (request.RequestUri.AbsoluteUri.StartsWith("https://graph.microsoft.com/v1.0/me"))
|
||||
{
|
||||
var queryParameters = request.RequestUri.ParseQueryString();
|
||||
if (queryParameters["access_token"] == "ValidAccessToken")
|
||||
if (request.Headers.Authorization.Parameter == "ValidAccessToken")
|
||||
{
|
||||
response.Content = new StringContent("{\r \"id\": \"fccf9a24999f4f4f\", \r \"name\": \"Owinauthtester Owinauthtester\", \r \"first_name\": \"Owinauthtester\", \r \"last_name\": \"Owinauthtester\", \r \"link\": \"https://profile.live.com/\", \r \"gender\": null, \r \"locale\": \"en_US\", \r \"updated_time\": \"2013-08-27T22:18:14+0000\"\r}");
|
||||
response.Content = new StringContent("{\r \"id\": \"fccf9a24999f4f4f\", \r \"displayName\": \"Owinauthtester Owinauthtester\", \r \"givenName\": \"Owinauthtester\", \r \"surname\": \"Owinauthtester\", \r \"link\": \"https://profile.live.com/\", \r \"gender\": null, \r \"locale\": \"en_US\", \r \"updated_time\": \"2013-08-27T22:18:14+0000\"\r}");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -65,7 +65,7 @@ namespace Microsoft.Owin.Security.Tests.MicrosoftAccount
|
|||
var transaction = await SendAsync(server, "http://example.com/challenge");
|
||||
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.Redirect);
|
||||
var location = transaction.Response.Headers.Location.AbsoluteUri;
|
||||
location.ShouldContain("https://login.live.com/oauth20_authorize.srf");
|
||||
location.ShouldContain("https://login.microsoftonline.com/common/oauth2/v2.0/authorize");
|
||||
location.ShouldContain("response_type=code");
|
||||
location.ShouldContain("client_id=");
|
||||
location.ShouldContain("redirect_uri=");
|
||||
|
@ -84,7 +84,7 @@ namespace Microsoft.Owin.Security.Tests.MicrosoftAccount
|
|||
{
|
||||
Sender = async req =>
|
||||
{
|
||||
if (req.RequestUri.AbsoluteUri == "https://login.live.com/oauth20_token.srf")
|
||||
if (req.RequestUri.AbsoluteUri == "https://login.microsoftonline.com/common/oauth2/v2.0/token")
|
||||
{
|
||||
return await ReturnJsonResponse(new
|
||||
{
|
||||
|
@ -94,18 +94,15 @@ namespace Microsoft.Owin.Security.Tests.MicrosoftAccount
|
|||
refresh_token = "Test Refresh Token"
|
||||
});
|
||||
}
|
||||
else if (req.RequestUri.GetLeftPart(UriPartial.Path) == "https://apis.live.net/v5.0/me")
|
||||
else if (req.RequestUri.GetLeftPart(UriPartial.Path) == "https://graph.microsoft.com/v1.0/me")
|
||||
{
|
||||
return await ReturnJsonResponse(new
|
||||
{
|
||||
id = "Test User ID",
|
||||
name = "Test Name",
|
||||
first_name = "Test Given Name",
|
||||
last_name = "Test Family Name",
|
||||
emails = new
|
||||
{
|
||||
preferred = "Test email"
|
||||
}
|
||||
displayName = "Test Name",
|
||||
givenName = "Test Given Name",
|
||||
surname = "Test Family Name",
|
||||
mail = "Test email"
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче