Ho trovato la tua domanda quando cercavo la soluzione per un problema simile. Ho risolto il problema creando 2 nuove classi, che puoi leggere in questo coderwall post.
sarò anche copiare e incollare l'articolo completo qui:
DotNetOpenAuth.AspNet 401 errore non autorizzato e persistente token di accesso segreto Fix
Nel progettare QuietThyme, il nostro manager nuvola Ebook, sapevamo che tutti odiano creare nuovi account tanto quanto noi. Abbiamo iniziato a cercare le librerie OAuth e OpenId che potevamo sfruttare per consentire l'accesso social. Abbiamo finito per utilizzare la libreria DotNetOpenAuth.AspNet
per l'autenticazione utente, perché supporta Microsoft, Twitter, Facebook, LinkedIn e Yahoo e molti altri proprio fuori dal campo. Mentre abbiamo riscontrato alcuni problemi nell'impostare tutto, alla fine abbiamo solo dovuto fare alcune piccole personalizzazioni per far funzionare la maggior parte di esso (descritto in un previous coderwall post).Abbiamo notato che, a differenza di tutti gli altri, il client di LinkedIn non si autenticava, restituendo un errore non autorizzato 401 da DotNetOpenAuth. È diventato subito evidente che ciò era dovuto a un problema di firma e, dopo aver esaminato la fonte, siamo stati in grado di determinare che il segreto AccessToken recuperato non viene utilizzato con la richiesta di informazioni sul profilo autenticato.
Rende acutally senso, la ragione che la classe OAuthClient non include l'accesso recuperata token segreto è che è normalmente non necessaria per l'autenticazione, che è lo scopo principale della biblioteca ASP.NET OAuth.
Avevamo bisogno di fare richieste autenticate contro l'API, dopo che l'utente ha effettuato l'accesso, per recuperare alcune informazioni del profilo standard, inclusi indirizzo e-mail e nome completo. Siamo stati in grado di risolvere questo problema utilizzando temporaneamente InMemoryOAuthTokenManager.
public class LinkedInCustomClient : OAuthClient
{
private static XDocument LoadXDocumentFromStream(Stream stream)
{
var settings = new XmlReaderSettings
{
MaxCharactersInDocument = 65536L
};
return XDocument.Load(XmlReader.Create(stream, settings));
}
/// Describes the OAuth service provider endpoints for LinkedIn.
private static readonly ServiceProviderDescription LinkedInServiceDescription =
new ServiceProviderDescription
{
AccessTokenEndpoint =
new MessageReceivingEndpoint("https://api.linkedin.com/uas/oauth/accessToken",
HttpDeliveryMethods.PostRequest),
RequestTokenEndpoint =
new MessageReceivingEndpoint("https://api.linkedin.com/uas/oauth/requestToken?scope=r_basicprofile+r_emailaddress",
HttpDeliveryMethods.PostRequest),
UserAuthorizationEndpoint =
new MessageReceivingEndpoint("https://www.linkedin.com/uas/oauth/authorize",
HttpDeliveryMethods.PostRequest),
TamperProtectionElements =
new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement() },
//ProtocolVersion = ProtocolVersion.V10a
};
private string ConsumerKey { get; set; }
private string ConsumerSecret { get; set; }
public LinkedInCustomClient(string consumerKey, string consumerSecret)
: this(consumerKey, consumerSecret, new AuthenticationOnlyCookieOAuthTokenManager()) { }
public LinkedInCustomClient(string consumerKey, string consumerSecret, IOAuthTokenManager tokenManager)
: base("linkedIn", LinkedInServiceDescription, new SimpleConsumerTokenManager(consumerKey, consumerSecret, tokenManager))
{
ConsumerKey = consumerKey;
ConsumerSecret = consumerSecret;
}
//public LinkedInCustomClient(string consumerKey, string consumerSecret) :
// base("linkedIn", LinkedInServiceDescription, consumerKey, consumerSecret) { }
/// Check if authentication succeeded after user is redirected back from the service provider.
/// The response token returned from service provider authentication result.
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
Justification = "We don't care if the request fails.")]
protected override AuthenticationResult VerifyAuthenticationCore(AuthorizedTokenResponse response)
{
// See here for Field Selectors API http://developer.linkedin.com/docs/DOC-1014
const string profileRequestUrl =
"https://api.linkedin.com/v1/people/~:(id,first-name,last-name,headline,industry,summary,email-address)";
string accessToken = response.AccessToken;
var profileEndpoint =
new MessageReceivingEndpoint(profileRequestUrl, HttpDeliveryMethods.GetRequest);
try
{
InMemoryOAuthTokenManager imoatm = new InMemoryOAuthTokenManager(ConsumerKey, ConsumerSecret);
imoatm.ExpireRequestTokenAndStoreNewAccessToken(String.Empty, String.Empty, accessToken, (response as ITokenSecretContainingMessage).TokenSecret);
WebConsumer w = new WebConsumer(LinkedInServiceDescription, imoatm);
HttpWebRequest request = w.PrepareAuthorizedRequest(profileEndpoint, accessToken);
using (WebResponse profileResponse = request.GetResponse())
{
using (Stream responseStream = profileResponse.GetResponseStream())
{
XDocument document = LoadXDocumentFromStream(responseStream);
string userId = document.Root.Element("id").Value;
string firstName = document.Root.Element("first-name").Value;
string lastName = document.Root.Element("last-name").Value;
string userName = firstName + " " + lastName;
string email = String.Empty;
try
{
email = document.Root.Element("email-address").Value;
}
catch(Exception)
{
}
var extraData = new Dictionary<string, string>();
extraData.Add("accesstoken", accessToken);
extraData.Add("name", userName);
extraData.AddDataIfNotEmpty(document, "headline");
extraData.AddDataIfNotEmpty(document, "summary");
extraData.AddDataIfNotEmpty(document, "industry");
if(!String.IsNullOrEmpty(email))
{
extraData.Add("email",email);
}
return new AuthenticationResult(
isSuccessful: true, provider: this.ProviderName, providerUserId: userId, userName: userName, extraData: extraData);
}
}
}
catch (Exception exception)
{
return new AuthenticationResult(exception);
}
}
}
Ecco la sezione che è stata modificata dal client di base di LinkedIn scritto da Microsoft.
InMemoryOAuthTokenManager imoatm = new InMemoryOAuthTokenManager(ConsumerKey, ConsumerSecret);
imoatm.ExpireRequestTokenAndStoreNewAccessToken(String.Empty, String.Empty, accessToken, (response as ITokenSecretContainingMessage).TokenSecret);
WebConsumer w = new WebConsumer(LinkedInServiceDescription, imoatm);
HttpWebRequest request = w.PrepareAuthorizedRequest(profileEndpoint, accessToken);
Purtroppo, il metodo IOAuthTOkenManger.ReplaceRequestTokenWithAccessToken(..)
non viene eseguito fino a quando il metodo VerifyAuthentication()
ritorni, così abbiamo invece necessario creare una nuova TokenManager ed e creare un WebConsumer
e HttpWebRequest
utilizzando le credenziali access token che abbiamo appena recuperati.
Questo risolve il nostro semplice numero 401 non autorizzato.
Ora cosa succede se si desidera mantenere le credenziali di AccessToken dopo il processo di autenticazione? Ciò potrebbe essere utile per un client DropBox, ad esempio, in cui si desidera sincronizzare i file con DropBox di un utente in modo asincrono. Il problema risale al modo in cui è stata scritta la libreria AspNet, si è ipotizzato che DotNetOpenAuth sarebbe stato utilizzato solo per l'autenticazione dell'utente, non come base per le chiamate api OAuth successive. Per fortuna la correzione era abbastanza semplice, tutto quello che dovevo fare era modificare la base AuthetnicationOnlyCookieOAuthTokenManger
in modo che il metodo ReplaceRequestTokenWithAccessToken(..)
memorizzasse la nuova chiave e i segreti di AccessToken.
/// <summary>
/// Stores OAuth tokens in the current request's cookie
/// </summary>
public class PersistentCookieOAuthTokenManagerCustom : AuthenticationOnlyCookieOAuthTokenManager
{
/// <summary>
/// Key used for token cookie
/// </summary>
private const string TokenCookieKey = "OAuthTokenSecret";
/// <summary>
/// Primary request context.
/// </summary>
private readonly HttpContextBase primaryContext;
/// <summary>
/// Initializes a new instance of the <see cref="AuthenticationOnlyCookieOAuthTokenManager"/> class.
/// </summary>
public PersistentCookieOAuthTokenManagerCustom() : base()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="AuthenticationOnlyCookieOAuthTokenManager"/> class.
/// </summary>
/// <param name="context">The current request context.</param>
public PersistentCookieOAuthTokenManagerCustom(HttpContextBase context) : base(context)
{
this.primaryContext = context;
}
/// <summary>
/// Gets the effective HttpContext object to use.
/// </summary>
private HttpContextBase Context
{
get
{
return this.primaryContext ?? new HttpContextWrapper(HttpContext.Current);
}
}
/// <summary>
/// Replaces the request token with access token.
/// </summary>
/// <param name="requestToken">The request token.</param>
/// <param name="accessToken">The access token.</param>
/// <param name="accessTokenSecret">The access token secret.</param>
public new void ReplaceRequestTokenWithAccessToken(string requestToken, string accessToken, string accessTokenSecret)
{
//remove old requestToken Cookie
//var cookie = new HttpCookie(TokenCookieKey)
//{
// Value = string.Empty,
// Expires = DateTime.UtcNow.AddDays(-5)
//};
//this.Context.Response.Cookies.Set(cookie);
//Add new AccessToken + secret Cookie
StoreRequestToken(accessToken, accessTokenSecret);
}
}
quindi di utilizzare questo PersistentCookieOAuthTokenManager
tutto quello che dovete fare è modificare il vostro costruttore DropboxClient, o qualsiasi altro client in cui si desidera a persistere l'access token segreto
public DropBoxCustomClient(string consumerKey, string consumerSecret)
: this(consumerKey, consumerSecret, new PersistentCookieOAuthTokenManager()) { }
public DropBoxCustomClient(string consumerKey, string consumerSecret, IOAuthTokenManager tokenManager)
: base("dropBox", DropBoxServiceDescription, new SimpleConsumerTokenManager(consumerKey, consumerSecret, tokenManager))
{}
So che c'è un modo più bello per formattare il codice, ma non riesco a trovarlo per tutta la vita. Fare clic sul pulsante del codice nella domanda non sembra funzionare. Se qualcuno vuole consigliarti su come sistemarlo, è molto apprezzato. –
La formattazione del codice è ora basata su tag e non hai tag specifici della lingua nel tuo post, quindi non ha fatto nulla. Ho aggiunto sopra il tuo codice per forzarlo a evidenziarlo. Vedi http://meta.stackexchange.com/a/128910/190311 –