本文介绍了如何让用户登录到使用的SoundCloud我的网站的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望让用户通过的SoundCloud为我的ASP.NET MVC 4项目进行身份验证。由于没有.NET SDK,我写了一个自定义的 OAuth2Client 来处理身份验证。添加客户端后,我的 AuthConfig.cs ,它恰当地表现了作为一个选项进行登录。问题是,当我点击按钮来登录,它总是返回

 登录失败。登录不成功的服务。

甚至没有问我的SoundCloud登录。有什么问题?我实现了一个非常类似的客户端GitHub上,并没有任何问题的工作。

下面是我的客户:

 公共类SoundCloudOAuth2Client:OAuth2Client
 {
     私人常量字符串ENDUSERAUTHLINK =htt​​ps://soundcloud.com/connect;
     私人常量字符串TOKENLINK =htt​​ps://api.soundcloud.com/oauth2/token;
     私人只读字符串_clientID;
     私人只读字符串_clientSecret;     公共SoundCloudOAuth2Client(字符串客户端ID,字符串clientSecret):基地(的SoundCloud)
     {
         如果(string.IsNullOrWhiteSpace(clientID的)){
                抛出新的ArgumentNullException(clientID的);
         }         如果(string.IsNullOrWhiteSpace(clientSecret)){
                抛出新的ArgumentNullException(clientSecret);
         }         _clientID = clientID的;
         _clientSecret = clientSecret;
     }     保护覆盖乌里GetServiceLoginUrl(URI RETURNURL)
     {
         StringBuilder的的serviceUrl =新的StringBuilder();
         serviceUrl.Append(ENDUSERAUTHLINK);
         serviceUrl.AppendFormat(?CLIENT_ID = {0},_clientID);
         serviceUrl.AppendFormat(与& RESPONSE_TYPE = {0},code);
         serviceUrl.AppendFormat(与&范围= {0},永不过期);
         serviceUrl.AppendFormat(与& REDIRECT_URI = {0},System.Uri.EscapeDataString(returnUrl.ToString()));         返回新的URI(serviceUrl.ToString());
     }     公共覆盖无效RequestAuthentication(HttpContextBase背景下,乌里RETURNURL)
     {
         base.RequestAuthentication(背景下,RETURNURL);
     }     保护覆盖的IDictionary<字符串,字符串> GetUserData(字符串的accessToken)
     {
         IDictionary的<字符串,字符串> extraData =新词典<字符串,字符串>();         VAR的WebRequest =(HttpWebRequest的)WebRequest.Create(https://api.soundcloud.com/me.json?oauth_token=+的accessToken);
         webRequest.Method =GET;
         串响应=;
         使用(HttpWebResponse WebResponse类= HttpWebResponse)webRequest.GetResponse())
         {
             使用(StreamReader的读者=新的StreamReader(webResponse.GetResponseStream()))
             {
                 响应= reader.ReadToEnd();
             }
         }         变种JSON = JObject.Parse(响应);
         字符串ID =(字符串)JSON [ID];
         字符串的用户名=(字符串)JSON [用户名];
         字符串permalinkUrl =(字符串)JSON [permalink_url];         extraData =新词典<字符串,字符串>
         {
             {SCAccessToken的accessToken},
             {用户名,用户名}
             {permalinkUrl,permalinkUrl},
             {ID,ID}
         };         返回extraData;
     }     保护覆盖字符串QueryAccessToken(URI RETURNURL,串授权code)
     {
         StringBuilder的POSTDATA =新的StringBuilder();
         postData.AppendFormat(的client_id = {0},this._clientID);
         postData.AppendFormat(与& REDIRECT_URI = {0},HttpUtility.UrlEn code(returnUrl.ToString()));
         postData.AppendFormat(与& client_secret = {0},this._clientSecret);
         postData.AppendFormat(与& grant_type = {0},authorization_ code);
         postData.AppendFormat(与& code = {0},授权code);         串响应=;
         字符串的accessToken =;         VAR的WebRequest =(HttpWebRequest的)WebRequest.Create(TOKENLINK);
         webRequest.Method =POST;
         webRequest.ContentType =应用/的X WWW的形式urlen codeD;         使用(流S = webRequest.GetRequestStream())
         {
             使用(StreamWriter的SW =新的StreamWriter(S))
                    sw.Write(postData.ToString());
         }         使用(WebResponse类WebResponse类= webRequest.GetResponse())
         {
             使用(StreamReader的读者=新的StreamReader(webResponse.GetResponseStream()))
             {
                 响应= reader.ReadToEnd();
             }
         }         变种JSON = JObject.Parse(响应);
         的accessToken =(字符串)JSON [的access_token];         返回的accessToken;
     }     公众覆盖AuthenticationResult VerifyAuthentication(HttpContextBase背景下,乌里returnPageUrl)
     {
         字符串code = context.Request.QueryString [code];
         串U = context.Request.Url.ToString();         如果(string.IsNullOrEmpty(code)条)
         {
             返回AuthenticationResult.Failed;
         }         字符串的accessToken = this.QueryAccessToken(returnPageUrl,code);
         如果(的accessToken == NULL)
         {
             返回AuthenticationResult.Failed;
         }         IDictionary的<字符串,字符串>用户数据= this.GetUserData(的accessToken);
         如果(用户数据== NULL)
         {
             返回AuthenticationResult.Failed;
         }         字符串ID =用户数据[ID];
         字符串名称;         如果(userData.TryGetValue(用户名,出来的名字)及!&安培;!userData.TryGetValue(名,出来的名字))
         {
             名称= ID;
         }         返回新AuthenticationResult(
             isSuccessful:真实,供应商的SoundCloud,providerUserId:ID,用户名:姓名,extraData:USERDATA);
     }
 }

AuthConfig.cs

 公共静态无效RegisterAuth()
 {
     OAuthWebSecurity.RegisterClient(新SoundCloudOAuth2Client(
         clientID的:MyValues​​.MyClientID,
         clientSecret:MyValues​​.MyClientSECRET)
         显示名的SoundCloud
         extraData:NULL);     OAuthWebSecurity.RegisterClient(新GitHubOAuth2Client(
         APPID:MyValues​​.GITHUBAPPID,
         appSecret:MyValues​​.GITHUBAPPSECRET),GitHub上,NULL);     OAuthWebSecurity.RegisterGoogleClient();
     OAuthWebSecurity.RegisterYahooClient();
 }


解决方案

有多个问题需要解决,开始运行的第一个功能: GetServiceLoginUrl(URI RETURNURL)

RETURNURL ,将被自动创建,包含&符号,它的SoundCloud不喜欢。你需要剥离出&符号,并确保在你的SoundCloud帐户重定向URI进行验证的究竟的匹配是什么正在发送(查询字符串和所有)。这里是什么正在被默认发送的RETURNURL一个例子:

<$p$p><$c$c>https://localhost:44301/Account/ExternalLoginCallback?__provider__=SoundCloud&__sid__=blahblahyoursid

第一步是删除&放大器; __ __ SID 值。您可以去掉 SID 值,并将它传递为参数,以防万一你需要它状态。新功能如下:

 保护覆盖乌里GetServiceLoginUrl(URI RETURNURL)
{
    StringBuilder的的serviceUrl =新的StringBuilder();
    字符串SID =的String.Empty;
    如果(returnUrl.ToString()。包含(__ sid__))
    {
        INT指数= returnUrl.ToString()的IndexOf(__ sid__)+ 8。
        。INT LEN = returnUrl.ToString()长;
        SID = returnUrl.ToString()子串(索引,LEN - 指数-1)。
    }    。字符串redirectUri = returnUrl.ToString()包含('和;')?
    。returnUrl.ToString()子串(0,returnUrl.ToString()的IndexOf(与&amp;)。):
    returnUrl.ToString();
    serviceUrl.Append(ENDUSERAUTHLINK);
    serviceUrl.AppendFormat(?CLIENT_ID = {0},_clientID);
    serviceUrl.AppendFormat(与&amp; RESPONSE_TYPE = {0},code);
    serviceUrl.AppendFormat(与&amp;范围= {0},永不过期);
    serviceUrl.AppendFormat(与&amp;国家= {0},SID);
    serviceUrl.AppendFormat(与&amp; REDIRECT_URI = {0},System.Uri.EscapeDataString(redirectUri));    返回新的URI(serviceUrl.ToString());
}

这是解决问题的一部分。在SoundlCoud重定向URI现在简直是 https://开头本地主机:44301 /帐号/ ExternalLoginCallback __provider __ =的SoundCloud ?)。但是,试图验证仍然会返回。下一个问题解决的是在 AccountController.cs ,具体是:

  [使用AllowAnonymous]
公众的ActionResult ExternalLoginCallback(字符串RETURNURL)

,因为在第一线,就试图返回:

  AuthenticationResult结果= OAuthWebSecurity.VerifyAuthentication(Url.Action(ExternalLoginCallback,新的{RETURNURL = RETURNURL}));

和这不适合我的自定义 OAuth2Client ,因为 VerifyAuthentication 采用不同的参数运行。通过检测,如果它是客户端的SoundCloud修复它,然后使用定制VerifyAuthentication:

  [使用AllowAnonymous]
公众的ActionResult ExternalLoginCallback(字符串RETURNURL)
{
    AuthenticationResult结果;
    VAR语境= this.HttpContext;
    串P = Tools.GetProviderNameFromQueryString(context.Request.QueryString);    如果(String.IsNullOrEmpty(对)及!&放大器; p.ToLower()==的SoundCloud)
    {
        结果=新SoundCloudOAuth2Client(
                clientID的:MyValues​​.SCCLIENTID,
                clientSecret:MyValues​​.SCCLIENTSECRET).VerifyAuthentication(this.HttpContext,新的URI(的String.Format({0} /帐号/ ExternalLoginCallback __ __提供商的SoundCloud =,context.Request.Url.GetLeftPart(UriPartial.Authority)的ToString() )));
    }
    其他
    {
        结果= OAuthWebSecurity.VerifyAuthentication(Url.Action(ExternalLoginCallback,新的{RETURNURL = RETURNURL}));
    }

其中,

 公共静态字符串GetProviderNameFromQueryString(NameValueCollection中的queryString)
{
    VAR的结果=的queryString [__ provider__];
    ///注释掉的东西
    返回结果;
}

在这之后,一切工作正常,你可以成功进行身份验证。您可以配置 GetUserData 来得到任何的SoundCloud的数据要保存,然后关闭其保存到您的用户配置或相关表格。关键的是, SCAccessToken ,因为这是你将需要在未来如何上传到他们的帐户。

I want to let users authenticate via SoundCloud for my ASP.NET MVC 4 project. Since there is no .NET SDK, I wrote a custom OAuth2Client to handle the authentication. After adding the client to my AuthConfig.cs, it appropriately showed up as an option to login. The problem is, when I click on the button to login, it always returns

Login Failure.

Unsuccessful login with service.

without even asking me to login in SoundCloud. What is the problem? I implemented a very similar client for GitHub and it worked with no problems.

Here is my client:

 public class SoundCloudOAuth2Client : OAuth2Client
 {
     private const string ENDUSERAUTHLINK = "https://soundcloud.com/connect";
     private const string TOKENLINK = "https://api.soundcloud.com/oauth2/token";
     private readonly string _clientID;
     private readonly string _clientSecret;

     public SoundCloudOAuth2Client(string clientID, string clientSecret) : base("SoundCloud")
     {
         if (string.IsNullOrWhiteSpace(clientID)) {
                throw new ArgumentNullException("clientID");
         }

         if (string.IsNullOrWhiteSpace(clientSecret)) {
                throw new ArgumentNullException("clientSecret");
         }

         _clientID = clientID;
         _clientSecret = clientSecret;
     }

     protected override Uri GetServiceLoginUrl(Uri returnUrl)
     {
         StringBuilder serviceUrl = new StringBuilder();
         serviceUrl.Append(ENDUSERAUTHLINK);
         serviceUrl.AppendFormat("?client_id={0}", _clientID);
         serviceUrl.AppendFormat("&response_type={0}", "code");
         serviceUrl.AppendFormat("&scope={0}", "non-expiring");
         serviceUrl.AppendFormat("&redirect_uri={0}", System.Uri.EscapeDataString(returnUrl.ToString()));

         return new Uri(serviceUrl.ToString());
     }

     public override void RequestAuthentication(HttpContextBase context, Uri returnUrl)
     {
         base.RequestAuthentication(context, returnUrl);
     }

     protected override IDictionary<string, string> GetUserData(string accessToken)
     {
         IDictionary<String, String> extraData = new Dictionary<String, String>();

         var webRequest = (HttpWebRequest)WebRequest.Create("https://api.soundcloud.com/me.json?oauth_token=" + accessToken);
         webRequest.Method = "GET";
         string response = "";
         using (HttpWebResponse webResponse = HttpWebResponse)webRequest.GetResponse())
         {
             using (StreamReader reader = new StreamReader(webResponse.GetResponseStream()))
             {
                 response = reader.ReadToEnd();
             }
         }

         var json = JObject.Parse(response);
         string id = (string)json["id"];
         string username = (string)json["username"];
         string permalinkUrl = (string)json["permalink_url"];

         extraData = new Dictionary<String, String>
         {
             {"SCAccessToken", accessToken},
             {"username", username},
             {"permalinkUrl", permalinkUrl},
             {"id", id}
         };

         return extraData;
     }

     protected override string QueryAccessToken(Uri returnUrl, string authorizationCode)
     {
         StringBuilder postData = new StringBuilder();
         postData.AppendFormat("client_id={0}", this._clientID);
         postData.AppendFormat("&redirect_uri={0}", HttpUtility.UrlEncode(returnUrl.ToString()));
         postData.AppendFormat("&client_secret={0}", this._clientSecret);
         postData.AppendFormat("&grant_type={0}", "authorization_code");
         postData.AppendFormat("&code={0}", authorizationCode);

         string response = "";
         string accessToken = "";

         var webRequest = (HttpWebRequest)WebRequest.Create(TOKENLINK);
         webRequest.Method = "POST";
         webRequest.ContentType = "application/x-www-form-urlencoded";

         using (Stream s = webRequest.GetRequestStream())
         {
             using (StreamWriter sw = new StreamWriter(s))
                    sw.Write(postData.ToString());
         }

         using (WebResponse webResponse = webRequest.GetResponse())
         {
             using (StreamReader reader = new StreamReader(webResponse.GetResponseStream()))
             {
                 response = reader.ReadToEnd();
             }
         }

         var json = JObject.Parse(response);
         accessToken = (string)json["access_token"];

         return accessToken;
     }

     public override AuthenticationResult VerifyAuthentication(HttpContextBase context, Uri returnPageUrl)
     {
         string code = context.Request.QueryString["code"];
         string u = context.Request.Url.ToString();

         if (string.IsNullOrEmpty(code))
         {
             return AuthenticationResult.Failed;
         }

         string accessToken = this.QueryAccessToken(returnPageUrl, code);
         if (accessToken == null)
         {
             return AuthenticationResult.Failed;
         }

         IDictionary<string, string> userData = this.GetUserData(accessToken);
         if (userData == null)
         {
             return AuthenticationResult.Failed;
         }

         string id = userData["id"];
         string name;

         if (!userData.TryGetValue("username", out name) && !userData.TryGetValue("name", out name))
         {
             name = id;
         }

         return new AuthenticationResult(
             isSuccessful: true, provider: "SoundCloud", providerUserId: id, userName: name, extraData: userData);
     }
 }

and the AuthConfig.cs:

 public static void RegisterAuth()
 {
     OAuthWebSecurity.RegisterClient(new SoundCloudOAuth2Client(
         clientID: MyValues.MyClientID,
         clientSecret: MyValues.MyClientSECRET),
         displayName: "SoundCloud",
         extraData: null);

     OAuthWebSecurity.RegisterClient(new GitHubOAuth2Client(
         appId: MyValues.GITHUBAPPID,
         appSecret: MyValues.GITHUBAPPSECRET), "GitHub", null);

     OAuthWebSecurity.RegisterGoogleClient();
     OAuthWebSecurity.RegisterYahooClient();
 }
解决方案

There are multiple issues to address, starting with the first function that runs: GetServiceLoginUrl(Uri returnUrl)

The returnUrl, which is automatically created, contains ampersands, which SoundCloud does not like. You need to strip out the ampersands and ensure the "Redirect URI for Authentication" in your SoundCloud account exactly matches what is being sent (querystring and all). Here is an example of what was being sent as the returnURL by default:

https://localhost:44301/Account/ExternalLoginCallback?__provider__=SoundCloud&__sid__=blahblahyoursid

First step was to remove the &__sid__ value. You can strip out sid value and pass it as the state parameter, just in case you ever need it. The new function looks like this:

protected override Uri GetServiceLoginUrl(Uri returnUrl)
{
    StringBuilder serviceUrl = new StringBuilder();
    string sid = String.Empty;
    if (returnUrl.ToString().Contains("__sid__"))
    {
        int index = returnUrl.ToString().IndexOf("__sid__") + 8;
        int len = returnUrl.ToString().Length;
        sid = returnUrl.ToString().Substring(index, len - index-1);
    }

    string redirectUri = returnUrl.ToString().Contains('&') ?
    returnUrl.ToString().Substring(0,returnUrl.ToString().IndexOf("&")) :
    returnUrl.ToString();
    serviceUrl.Append(ENDUSERAUTHLINK);
    serviceUrl.AppendFormat("?client_id={0}", _clientID);
    serviceUrl.AppendFormat("&response_type={0}", "code");
    serviceUrl.AppendFormat("&scope={0}", "non-expiring");
    serviceUrl.AppendFormat("&state={0}", sid);
    serviceUrl.AppendFormat("&redirect_uri={0}", System.Uri.EscapeDataString(redirectUri));

    return new Uri(serviceUrl.ToString());
}

That solves part of the problem. The redirect URI in SoundlCoud now is simply https://localhost:44301/Account/ExternalLoginCallback?__provider__=SoundCloud). But trying to authenticate will still return false. The next issue to address is in AccountController.cs, specifically:

[AllowAnonymous]
public ActionResult ExternalLoginCallback(string returnUrl)

because in the first line, it tries to return:

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

and this doesn't run for my custom OAuth2Client, since VerifyAuthentication takes different parameters. Fix it by detecting if it is the SoundCloud client and then use the custom VerifyAuthentication:

[AllowAnonymous]
public ActionResult ExternalLoginCallback(string returnUrl)
{
    AuthenticationResult result;
    var context = this.HttpContext;
    string p = Tools.GetProviderNameFromQueryString(context.Request.QueryString);

    if (!String.IsNullOrEmpty(p) && p.ToLower() == "soundcloud")
    {
        result = new SoundCloudOAuth2Client(
                clientID: MyValues.SCCLIENTID,
                clientSecret: MyValues.SCCLIENTSECRET).VerifyAuthentication(this.HttpContext, new Uri(String.Format("{0}/Account/ExternalLoginCallback?__provider__=SoundCloud", context.Request.Url.GetLeftPart(UriPartial.Authority).ToString())));
    }
    else
    {
        result = OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
    }

where

public static string GetProviderNameFromQueryString(NameValueCollection queryString)
{
    var result = queryString["__provider__"];
    ///commented out stuff
    return result;
}

After that, everything works fine and you can successfully authenticate. You can configure GetUserData to get whatever SoundCloud data you want to save and then save it off to your UserProfile or related table. The key part is that SCAccessToken because that is what you will need in the future to upload to their account.

这篇关于如何让用户登录到使用的SoundCloud我的网站的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-31 21:52