从oauth身份验证中获取电子邮件(Microsoft)

时间:2013-06-18 12:01:41

标签: asp.net-mvc-4 oauth-2.0

如何从微软帐户收到电子邮件?我正在做以下事情:

    public ActionResult ExternalLoginCallback(string returnUrl)
    {
    AuthenticationResult result = OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
//...

string email = null;
                if (result.Provider.ToLower() == "google")
                {
                    email = result.ExtraData["email"];
                }
                else if (result.Provider.ToLower() == "facebook")
                {
                    email = result.ExtraData["username"];
                }
                else if (result.Provider.ToLower() == "microsoft")
                {
                    email = result.ExtraData["????"];
                }    
}

对于谷歌和脸书我可以收到电子邮件,但我不能用微软?我应该使用什么?

3 个答案:

答案 0 :(得分:4)

解决方案:

public class MicrosoftScopedClient : IAuthenticationClient
    {
        private string clientId;
        private string clientSecret;
        private string scope;

        private const string baseUrl = "https://login.live.com/oauth20_authorize.srf";
        private const string tokenUrl = "https://login.live.com/oauth20_token.srf";

        public MicrosoftScopedClient(string clientId, string clientSecret, string scope)
        {
            this.clientId = clientId;
            this.clientSecret = clientSecret;
            this.scope = scope;
        }

        public string ProviderName
        {
            get { return "Microsoft"; }
        }

        public void RequestAuthentication(HttpContextBase context, Uri returnUrl)
        {
            string url = baseUrl + "?client_id=" + clientId + "&redirect_uri=" + HttpUtility.UrlEncode(returnUrl.ToString()) + "&scope=" + HttpUtility.UrlEncode(scope) + "&response_type=code";
            context.Response.Redirect(url);
        }

        public AuthenticationResult VerifyAuthentication(HttpContextBase context)
        {
            string code = context.Request.QueryString["code"];

            string rawUrl = context.Request.Url.ToString();
            //From this we need to remove code portion
            rawUrl = Regex.Replace(rawUrl, "&code=[^&]*", "");

            IDictionary<string, string> userData = GetUserData(code, rawUrl);

            if (userData == null)
                return new AuthenticationResult(false, ProviderName, null, null, null);

            string id = userData["id"];
            string username = userData["email"];
            userData.Remove("id");
            userData.Remove("email");

            AuthenticationResult result = new AuthenticationResult(true, ProviderName, id, username, userData);
            return result;
        }

        private IDictionary<string, string> GetUserData(string accessCode, string redirectURI)
        {
            string token = QueryAccessToken(redirectURI, accessCode);
            if (token == null || token == "")
            {
                return null;
            }
            var userData = GetUserData(token);
            return userData;
        }

        private IDictionary<string, string> GetUserData(string accessToken)
        {
            ExtendedMicrosoftClientUserData graph;
            var request =
                WebRequest.Create(
                    "https://apis.live.net/v5.0/me?access_token=" + EscapeUriDataStringRfc3986(accessToken));
            using (var response = request.GetResponse())
            {
                using (var responseStream = response.GetResponseStream())
                {
                    using (StreamReader sr = new StreamReader(responseStream))
                    {
                        string data = sr.ReadToEnd();
                        graph = JsonConvert.DeserializeObject<ExtendedMicrosoftClientUserData>(data);
                    }
                }
            }

            var userData = new Dictionary<string, string>();
            userData.Add("id", graph.Id);
            userData.Add("username", graph.Name);
            userData.Add("name", graph.Name);
            userData.Add("link", graph.Link == null ? null : graph.Link.AbsoluteUri);
            userData.Add("gender", graph.Gender);
            userData.Add("firstname", graph.FirstName);
            userData.Add("lastname", graph.LastName);
            userData.Add("email", graph.Emails.Preferred);
            return userData;
        }

        private string QueryAccessToken(string returnUrl, string authorizationCode)
        {
            var entity =
                CreateQueryString(
                    new Dictionary<string, string> {
                        { "client_id", this.clientId },
                        { "redirect_uri", returnUrl },
                        { "client_secret", this.clientSecret},
                        { "code", authorizationCode },
                        { "grant_type", "authorization_code" },
                    });

            WebRequest tokenRequest = WebRequest.Create(tokenUrl);
            tokenRequest.ContentType = "application/x-www-form-urlencoded";
            tokenRequest.ContentLength = entity.Length;
            tokenRequest.Method = "POST";

            using (Stream requestStream = tokenRequest.GetRequestStream())
            {
                var writer = new StreamWriter(requestStream);
                writer.Write(entity);
                writer.Flush();
            }

            HttpWebResponse tokenResponse = (HttpWebResponse)tokenRequest.GetResponse();
            if (tokenResponse.StatusCode == HttpStatusCode.OK)
            {
                using (Stream responseStream = tokenResponse.GetResponseStream())
                {
                    using (StreamReader sr = new StreamReader(responseStream))
                    {
                        string data = sr.ReadToEnd();
                        var tokenData = JsonConvert.DeserializeObject<OAuth2AccessTokenData>(data);
                        if (tokenData != null)
                        {
                            return tokenData.AccessToken;
                        }
                    }
                }
            }

            return null;
        }

        private static readonly string[] UriRfc3986CharsToEscape = new[] { "!", "*", "'", "(", ")" };
        private static string EscapeUriDataStringRfc3986(string value)
        {
            StringBuilder escaped = new StringBuilder(Uri.EscapeDataString(value));

            // Upgrade the escaping to RFC 3986, if necessary.
            for (int i = 0; i < UriRfc3986CharsToEscape.Length; i++)
            {
                escaped.Replace(UriRfc3986CharsToEscape[i], Uri.HexEscape(UriRfc3986CharsToEscape[i][0]));
            }

            // Return the fully-RFC3986-escaped string.
            return escaped.ToString();
        }

        private static string CreateQueryString(IEnumerable<KeyValuePair<string, string>> args)
        {
            if (!args.Any())
            {
                return string.Empty;
            }
            StringBuilder sb = new StringBuilder(args.Count() * 10);

            foreach (var p in args)
            {
                sb.Append(EscapeUriDataStringRfc3986(p.Key));
                sb.Append('=');
                sb.Append(EscapeUriDataStringRfc3986(p.Value));
                sb.Append('&');
            }
            sb.Length--; // remove trailing &

            return sb.ToString();
        }

        protected class ExtendedMicrosoftClientUserData
        {
            public string FirstName { get; set; }
            public string Gender { get; set; }
            public string Id { get; set; }
            public string LastName { get; set; }
            public Uri Link { get; set; }
            public string Name { get; set; }
            public Emails Emails { get; set; }
        }

        protected class Emails
        {
            public string Preferred { get; set; }
            public string Account { get; set; }
            public string Personal { get; set; }
            public string Business { get; set; }
        }
    }

<强> AuthConfig.cs

public static class AuthConfig
    {
        public static void RegisterAuth()
        {

            Dictionary<string, object> MicrosoftsocialData = new Dictionary<string, object>();
            MicrosoftsocialData.Add("Icon", "../Content/icons/microsoft.png");

            OAuthWebSecurity.RegisterClient(new MicrosoftScopedClient("XXXXXXXX", "YYYYYYYYYYYYY",
                "wl.basic wl.emails"), "Microsoft", MicrosoftsocialData);

            //......
        }
    }

用法:

public ActionResult ExternalLoginCallback(string returnUrl)
    {
    AuthenticationResult result = OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
//...

string email = null;
                if (result.Provider.ToLower() == "google")
                {
                    email = result.ExtraData["email"];
                }
                else if (result.Provider.ToLower() == "facebook")
                {
                    email = result.ExtraData["username"];
                }
                else if (result.Provider.ToLower() == "microsoft")
                {
                    email = result.UserName;
                }    
}

基于:How OAuthWebSecurity to obtain emails for different oauth clients, but Microsoft Client doesn’t return email, it didn’t include scope “wl.emails”

答案 1 :(得分:4)

甚至更简单:https://stackoverflow.com/a/22723713/1586498

var mo =
            new Microsoft.Owin.Security.MicrosoftAccount.MicrosoftAccountAuthenticationOptions
            {
                CallbackPath = new Microsoft.Owin.PathString("/Callbacks/External"),//register at oAuth provider
                ClientId = "<<yourclientid>>",
                ClientSecret = "<<yourclientsecret>>",
                Provider = new Microsoft.Owin.Security.MicrosoftAccount.MicrosoftAccountAuthenticationProvider
                {
                    OnAuthenticated = (context) =>
                        {
                            context.Identity.AddClaim(new Claim(providerKey, context.Identity.AuthenticationType));
                            context.Identity.AddClaim(new Claim(ClaimTypes.Name, context.Identity.FindFirstValue(ClaimTypes.Name)));
                            return System.Threading.Tasks.Task.FromResult(0);
                        }
                }
            };
mo.Scope.Add("wl.basic");  
mo.Scope.Add("wl.emails");  //HERE IS THE GOLD

app.UseMicrosoftAccountAuthentication(mo);

和我抓住他们的方式:

var externalIdentity = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);
externalIdentity.Claims.FirstOrDefault(c => c.Type.Equals(ClaimTypes.Email));

答案 2 :(得分:0)

amp的答案真的帮助了我。

另请注意,您在注册应用程序时必须选中“Live SDK support”复选框(https://apps.dev.microsoft.com/) - 否则OAuth服务会抱怨您没有客户机密(即使您做)。

只是想在没有使用AuthConfig.cs的情况下添加如何执行此操作以防任何人感兴趣(更多手册,但如果您不熟悉框架,它会更容易理解):

public ActionResult LoginWithMicrosoftAccount(CancellationToken cancellationToken)
{
    var client = new MicrosoftScopedClient(appID, appsecret, "wl.basic wl.emails");
    var urlNoQueryString = Request.Url.GetLeftPart(UriPartial.Path);

    AuthenticationResult result = null;
    if(Request.QueryString["error"]!= null)
    {//Microsoft service returns error
        return View();
    }
    if (Request.QueryString["code"] != null)
    {
        result = client.VerifyAuthentication(this.HttpContext);

        //at this point, you should get the username from result.UserName
    }
    if(Request.QueryString["code"]==null || result.UserName == null)
    {//will do the redirection
        client.RequestAuthentication(this.HttpContext, new Uri(urlNoQueryString));
    }
    return View();
}