i am facing this error, right now in oauthusing linq to twitter library:
<?xml version="1.0" encoding="UTF-8"?>
<hash>
<error>Required oauth_verifier parameter not provided</error>
<request>/oauth/access_token</request>
</hash>
i have followed this documentation link: https://linqtotwitter.codeplex.com/wikipage?title=Implementing%20OAuth%20for%20ASP.NET%20Web%20Forms&referringTitle=Learning%20to%20use%20OAuth
to implement the OAuth process, I get the this error at following line:
await auth.CompleteAuthorizeAsync(new Uri(twitterCallbackUrl));
below is the full code, please help me on this:
AspNetAuthorizer auth;
string twitterCallbackUrl = "http://127.0.0.1:58192/Default.aspx";
protected async void Page_Load(object sender, EventArgs e)
{
auth = new AspNetAuthorizer
{
CredentialStore = new SessionStateCredentialStore
{
ConsumerKey = ConfigurationManager.AppSettings["consumerKey"],
ConsumerSecret = ConfigurationManager.AppSettings["consumerSecret"]
},
GoToTwitterAuthorization =
twitterUrl => Response.Redirect(twitterUrl, false)
};
if (!Page.IsPostBack && Request.QueryString["oauth_token"] != null)
{
__await auth.CompleteAuthorizeAsync(new Uri(twitterCallbackUrl));__
// This is how you access credentials after authorization.
// The oauthToken and oauthTokenSecret do not expire.
// You can use the userID to associate the credentials with the user.
// You can save credentials any way you want - database, isolated
// storage, etc. - it's up to you.
// You can retrieve and load all 4 credentials on subsequent queries
// to avoid the need to re-authorize.
// When you've loaded all 4 credentials, LINQ to Twitter will let you
// make queries without re-authorizing.
//
var credentials = auth.CredentialStore;
string oauthToken = credentials.OAuthToken;
string oauthTokenSecret = credentials.OAuthTokenSecret;
string screenName = credentials.ScreenName;
ulong userID = credentials.UserID;
//Response.Redirect("~/Default.aspx", false);
}
}
protected async void AuthorizeButton_Click(object sender, EventArgs e)
{
await auth.BeginAuthorizeAsync(new Uri(twitterCallbackUrl));
//await auth.BeginAuthorizeAsync(Request.Url);
}
The problem occurs because your custom URL doesn't include the parameters that Twitter returned after the application requested authorization. If you set a breakpoint on CompleteAuthorizationAsync and type Request.Url into the Immediate window, you'll see these parameters:
If you still want to manually specify your URL, you need to include these parameters. Here's one way to do that:
string completeOAuthUrl = twitterCallbackUrl + Request.Url.Query;
await auth.CompleteAuthorizeAsync(completeOAuthUrl);
Alternatively, you can just use the page URL because that will already contains the proper parameters:
await auth.CompleteAuthorizeAsync(Request.Url);
Related
I recently posted a question which has been answered but led to this new problem. If interested, it can be seen at Previous post.
Intro
I am currently developing an application using AD-B2C as my identity provider. This is integrated into the solution using their guidelines at AD B2C graph, which uses openid-connect.
I need to use a form of email activation (outside of their register policy) and as such I need to be able to pass a value from the URL in the email, through the sign-up process at B2C and back to the redirection URL.
For this we use the state parameter.
Problem
In my OnRedirectToIdentityProvider I encrypt the state
private Task OnRedirectToIdentityProvider(RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
var temp = notification.ProtocolMessage.State;
// To be used later
var mycustomparameter = notification.OwinContext.Get<string>("mycustomparameter");
if (notification.ProtocolMessage.State != null)
{
var stateQueryString = notification.ProtocolMessage.State.Split('=');
var protectedState = stateQueryString[1];
var state = notification.Options.StateDataFormat.Unprotect(protectedState);
state.Dictionary.Add("mycustomparameter", "testing");
notification.ProtocolMessage.State = stateQueryString[0] + "=" + notification.Options.StateDataFormat.Protect(state);
}
return Task.FromResult(0);
}
This works for all I can tell.
Now the user is passed to the sign in on the AD B2C and is after the login redirected back where the OnMessageReceived is triggered.
private Task OnMessageReceived(MessageReceivedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
string mycustomparameter;
var protectedState = notification.ProtocolMessage.State.Split('=')[1];
var state = notification.Options.StateDataFormat.Unprotect(protectedState);
state.Dictionary.TryGetValue("mycustomparameter", out mycustomparameter);
return Task.FromResult(0);
}
this is where it breaks. In the ...StateDataFormat.Unprotect(protectedState)
It throws an error System.Security.Cryptography.CryptographicException with the message "Error occurred during a cryptographic operation."
EDIT: Stacktrace:
System.Web.dll!System.Web.Security.Cryptography.HomogenizingCryptoServiceWrapper.HomogenizeErrors(System.Func<byte[], byte[]> func, byte[] input) Unknown
System.Web.dll!System.Web.Security.Cryptography.HomogenizingCryptoServiceWrapper.Unprotect(byte[] protectedData) Unknown
System.Web.dll!System.Web.Security.MachineKey.Unprotect(System.Web.Security.Cryptography.ICryptoServiceProvider cryptoServiceProvider, byte[] protectedData, string[] purposes) Unknown
System.Web.dll!System.Web.Security.MachineKey.Unprotect(byte[] protectedData, string[] purposes) Unknown
Microsoft.Owin.Host.SystemWeb.dll!Microsoft.Owin.Host.SystemWeb.DataProtection.MachineKeyDataProtector.Unprotect(byte[] protectedData) Unknown
Microsoft.Owin.Security.dll!Microsoft.Owin.Security.DataProtection.AppBuilderExtensions.CallDataProtectionProvider.CallDataProtection.Unprotect(byte[] protectedData) Unknown
Microsoft.Owin.Security.dll!Microsoft.Owin.Security.DataHandler.SecureDataFormat<Microsoft.Owin.Security.AuthenticationProperties>.Unprotect(string protectedText) Unknown
IntellifyPortal.dll!IntellifyPortal.Startup.OnMessageReceived(Microsoft.Owin.Security.Notifications.MessageReceivedNotification notification) Line 171 C#
My attempts
I have tried specifying machine keys in the Web.config
I have tried messing with the "CallbackPath property in OpenIdConnectAuthenticationOptions, with no success.
I have tried a lot of diffent tweaks, but I can't seem to figure out why I can't "unprotect" the inbound state.
Any help is appreciated,
Best regards.
Update: Solution
I have decided to use an alternative method, which I found to work(hopefully it may of use to others):
Azure-sample which I used as guidance
private Task OnRedirectToIdentityProvider(RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
var policy = notification.OwinContext.Get<string>("Policy");
if (!string.IsNullOrEmpty(policy) && !policy.Equals(DefaultPolicy))
{
notification.ProtocolMessage.Scope = OpenIdConnectScopes.OpenId;
notification.ProtocolMessage.ResponseType = OpenIdConnectResponseTypes.IdToken;
notification.ProtocolMessage.IssuerAddress = notification.ProtocolMessage.IssuerAddress.ToLower().Replace(DefaultPolicy.ToLower(), policy.ToLower());
}
// Accept Invitation Email
string testValue= notification.OwinContext.Get<string>("testValue");
string testValue2= notification.OwinContext.Get<string>("testValue2");
if (!string.IsNullOrEmpty(testValue) && !string.IsNullOrEmpty(testValue2))
{
var stateQueryString = notification.ProtocolMessage.State.Split('=');
var protectedState = stateQueryString[1];
var state = notification.Options.StateDataFormat.Unprotect(protectedState);
state.Dictionary.Add("testValue", testValue);
state.Dictionary.Add("testValue2", testValue2);
notification.ProtocolMessage.State = stateQueryString[0] + "=" + notification.Options.StateDataFormat.Protect(state);
}
return Task.FromResult(0);
}
private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification notification)
{
// Extract the code from the response notification
var code = notification.Code;
string signedInUserID = notification.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
TokenCache userTokenCache = new MSALSessionCache(signedInUserID, notification.OwinContext.Environment["System.Web.HttpContextBase"] as HttpContextBase).GetMsalCacheInstance();
ConfidentialClientApplication cca = new ConfidentialClientApplication(ClientId, Authority, RedirectUri, new ClientCredential(ClientSecret), userTokenCache, null);
try
{
AuthenticationResult result = await cca.AcquireTokenByAuthorizationCodeAsync(code, Scopes);
// Look for acceptInvitation
string testValue;
string testValue2;
var protectedState = notification.ProtocolMessage.State.Split('=')[1];
var state = notification.Options.StateDataFormat.Unprotect(protectedState);
state.Dictionary.TryGetValue("testValue", out testValue);
state.Dictionary.TryGetValue("testValue2", out testValue2);
// InvitationAccept / store values
if(!string.IsNullOrEmpty(testValue) && !string.IsNullOrEmpty(testValue2))
{
// How can I pass values to the redirect controller?
// Can I somehow transfer it from here to that destination
}
}
catch (Exception ex)
{
//TODO: Handle
throw;
}
}
Final Question
I can now receive the values back as expected. These values has to be used in creating a relation between the new account and other accounts/groups in the application.
I therefore want to transfer these values back to the application (controller) for processing. I've tried storing the values in the context, in the response headers and in the claims to no avail. I guess this is because that this is the "middleware" and that the actual "redirect" happens directly from AD B2C thus not holding my params.
Can I somehow get the params back to the controller as well, without relying on the request URI (originating from the original user link) - Preferably directly in the claims, so that a user already logged in does not have to "re-signin" upon clicking the link.
How can I get my values (in the state, which are handled in the OnMessageRecieved) passed to the controller which is redirected to?
You're not supposed to decrypt the hint. Instead of this:
ProtocolMessage.State.Split('
Remove the hint so you only have encrypted data:
ProtocolMessage.State.Parameters["state"].Replace("OpenId.AuthenticationOptions=","")
Then you can you decrypt value of sate:
StateDataFormat.Unprotect("TC%$t43tj9358utj3")
It should deserialize to AuthenticationOptions.
I want to authenticate a user from my wp8 app using users google login credentials. So that I can get profile info of user. I found two articles in web with source code. But I was unable to get what I want.
First code I've found in this Link. But after getting authentication code it didn't have any code to get profile. May be I could not understand.
Second code I've found in this Link. It was following mvvm pattern, so I was totally blank to understand this code.
If anyone have used it properly, please help me. What actually I want that after getting client id and client secret what to do in app to get user's profile info. Helps are appreciated. Thanks in advance.
Here is code
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
IDictionary<string, string> parameters = this.NavigationContext.QueryString;
string authEndpoint = parameters["authEndpoint"];
string clientId = parameters["clientId"];
string scope = parameters["scope"];
string uri = string.Format("{0}?response_type=code&client_id={1}&redirect_uri={2}&scope={3}",
authEndpoint,
clientId,
"urn:ietf:wg:oauth:2.0:oob",
scope);
webBrowser.Navigate(new Uri(uri, UriKind.Absolute));
}
private async void LayoutRoot_Loaded(object sender, RoutedEventArgs e)
{
if(!App.loggedin)
{
OAuthAuthorization authorization = new OAuthAuthorization(
"https://accounts.google.com/o/oauth2/auth",
"https://accounts.google.com/o/oauth2/token");
TokenPair tokenPair = await authorization.Authorize(
"YOUR_CLIENT_ID",
"CLIENT_SECRET",
new string[] { GoogleScopes.UserinfoEmail });
// Request a new access token using the refresh token (when the access token was expired)
TokenPair refreshTokenPair = await authorization.RefreshAccessToken(
"YOUR_CLIENT_ID",
"CLIENT_SECRET",
tokenPair.RefreshToken);
}
}
what to do after getting access token?
This is the code that allows you to view the profile details:
private void LoadProfile(string access_token)
{
Debug.WriteLine("loading profile");
RestClient client = new RestClient("https://www.googleapis.com");
client.Authenticator = new OAuth2AuthorizationRequestHeaderAuthenticator(access_token);
var request = new RestRequest("/oauth2/v1/userinfo", Method.GET);
client.ExecuteAsync<Profile>(request, ProfileLoaded);
}
private void ProfileLoaded(IRestResponse<Profile> response)
{
Profile = response.Data;
}
Just pass in the access_token you got from your prior code and the data should be contained in response.Data
I have an interesting issue... For my windows phone 8.1 universal app, I call a method (CommAuthState), defined in my class, from my code:
Here is truncated code which is calling this method.
public async void ShowInitialPosts(object sender, RoutedEventArgs e)
{
#if WINDOWS_PHONE_APP
Windows.Phone.UI.Input.HardwareButtons.BackPressed += HardwareButtons_BackPressed;
#endif
//Get reference to the App setting container...
Windows.Storage.ApplicationDataContainer appRoamingSettings = Windows.Storage.ApplicationData.Current.RoamingSettings;
//Get the value for the logged in state and remembering option....
string commLoggedInValue = (string)appRoamingSettings.Values["CommLoggedIn"];
//If not logged in, redirect to the Community sign in page...
if (commLoggedInValue != "Yes")
{
this.Frame.Navigate(typeof(CommSignIn));
}
else if (commLoggedInValue == "Yes")
{
await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
//Check the access token and its validity...
forum_Hepler.CommAuthState(errorMessage, pgbar, pgText, ServerNetworkError);
});
//Get the access token value...
string myAccessTokenValue = (string)appRoamingSettings.Values["MyAccessToken"];
//Set the access token to the page variable...
accesstoken = myAccessTokenValue;
// Show the progress bar
mycontrols.progressbarShow(pgbar, pgText);
//Set the initial forum URL and page index..
downloadedPostsIndex = 0;
forumUrl = forumUrl + downloadedPostsIndex;
// Make a REST API call with Oauth token........
string telligentResult = await forum_Hepler.MyForumPostsOauth(forumUrl, accesstoken, errorMessage);
.....
.......
}
Here is my class method:
public async void CommAuthState(string errormessage, ProgressBar myprogressbar, TextBlock mytextblock, TextBlock myservernetworkerror) {
// Start showing the progress bar...
mycontrols.progressbarShow(myprogressbar, mytextblock);
//Get the access token value...
string myAccessTokenValue = (string) appRoamingSettings.Values["MyAccessToken"];
//Get the refresh token value...
string myRefreshTokenValue = (string) appRoamingSettings.Values["MyRefreshToken"];
//Get the original access token obtain time....
long myAccessTokenObtainedTimeValue = (long) appRoamingSettings.Values["MyAccessTokenObtainedTime"];
//Convertig date/time back to DateTime object....
origAccessTokenObtainedTime = DateTime.FromBinary(myAccessTokenObtainedTimeValue);
currentDateTime = DateTime.Now;
//Check to see if access token has expired....
accessTokenTimeElasped = currentDateTime - origAccessTokenObtainedTime;
accessTokenTimeElapsedSecs = accessTokenTimeElasped.TotalSeconds;
//Get the value of the access token expiration....
int MyAccessTokenExpiresValue = (int) appRoamingSettings.Values["MyAccessTokenExpires"];
if (accessTokenTimeElapsedSecs <= MyAccessTokenExpiresValue) {
//Get the long GUID value to be used for the GetOauthStuff function below...
string myLongGuidValue = (string) appRoamingSettings.Values["MyLongGuid"];
//Make a GET call to the OAUTH endpoint to get another Access Token by sending the refresh token ....
// string telligentOauthResult = await GetOauthStuff(oauthEndPoint, MyLongGuidValue, keyName, keySecret, errormessage);
string telligentOauthResult = await GetRefreshedOauthStuff(oauthEndPoint, myLongGuidValue, myRefreshTokenValue, keyName, keySecret, errormessage);
if (telligentOauthResult != null) {
// Creating a list out of the JSON object returned by the Telligent API endpoint...
ForumHelper.OAuthObject mytelligentResult = JsonConvert.DeserializeObject < ForumHelper.OAuthObject > (telligentOauthResult);
var accessToken = mytelligentResult.OAuthToken.access_token;
// Get the access token and the time and save it to settings
accessTokenObtainedTime = DateTime.Now;
var accessTokenError = mytelligentResult.OAuthToken.error;
var accessTokenTime = mytelligentResult.OAuthToken.expires_in;
var refreshToken = mytelligentResult.OAuthToken.refresh_token;
//Save access token to the app settings...
appRoamingSettings.Values["MyAccessToken"] = accessToken;
//Converting to binary format before saving since app settings cannot save in DateTime format and also we can convert it back later to the DateTime object
appRoamingSettings.Values["MyAccessTokenObtainedTime"] = accessTokenObtainedTime.ToBinary();
} else {
// Stop showing the progress bar...
mycontrols.progressbarNoShow(myprogressbar, mytextblock);
//Show the error message...
myservernetworkerror.Visibility = Windows.UI.Xaml.Visibility.Visible;
}
}
}
All works fine and I can step through my code to see how it is functioning till I get into the GetRefreshedOauthStuff method. here is the GetRefreshedOauthStuff method for reference:
public async Task < string > GetRefreshedOauthStuff(string url, string MyGUID, string MyResfreshToken, string KeyName, string KeySecret, string ErrorMessage) {
//Setting up Httpclient to send to the Telligent API call.....
var client = new System.Net.Http.HttpClient();
var adminKey = String.Format("{0}:{1}", KeySecret, KeyName);
var adminKeyBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(adminKey));
// Send customized headers with the token and impersonation request
client.DefaultRequestHeaders.Add("Rest-User-Token", adminKeyBase64);
client.DefaultRequestHeaders.Add("Cache-Control", "no-cache");
//Adding the long GUID to the OAUTH endpoint URL...
url = url + MyGUID + "&refresh_token=" + MyResfreshToken;
//For getting the list of posts....
System.Net.Http.HttpResponseMessage telligentResponse = await client.GetAsync(url);
if (telligentResponse.StatusCode == System.Net.HttpStatusCode.OK || telligentResponse.StatusCode == System.Net.HttpStatusCode.Forbidden) {
// Read the HTTP response content....
HttpContent responseContent = telligentResponse.Content;
// Read the response content as string.....
return await responseContent.ReadAsStringAsync();
} else {
throw new Exception("Error connecting to " + url + " ! Status: " + telligentResponse.StatusCode);
//Pop up the server or network issue message...
//mycontrols.popupMessages(ErrorMessage, "Network or Server error!");
////Navigate back to the main page....
//this.rootFrame.Navigate(typeof(MainPage));
//return null;
}
}
As soon as my code executes this line:
System.Net.Http.HttpResponseMessage telligentResponse = await client.GetAsync(url);
it steps out of this method instead of fully completing it and goes to this line in my starting method ShowInitialPosts:
string telligentResult = await forum_Hepler.MyForumPostsOauth(forumUrl, accesstoken, errorMessage);
This obviously creates problems and fails my logic. what am I doing wrong here? How do I fix this?
Any help or pointers are much appreciated
Thanks
That's exactly what await is supposed to do. It doesn't block the thread from executing - it tells the method to return control to whoever called it and then resume execution when the async method has finished.
Eric Lippert has a great blog post about this.
http://blogs.msdn.com/b/ericlippert/archive/2010/10/29/asynchronous-programming-in-c-5-0-part-two-whence-await.aspx
Assume we have an application that wants access popular Russian social network VK and written on C# with WinForms GUI. VK uses OAuth2-similiar approach, so we need to open web browser with vk oauth authorization url. Then we subscribe to webBrowser's OnNavigated event and waiting until url will not be equal some pre-defined url with access token in query string.
From now on we can call vk methods using received access token, but some strange things take place here: when i try to invoke some vk methods with HttpClient.GetAsync(methodUri), everything goes according to plan, except to opening the link from the authorization web browser in the system web browser.
vk's client authorization Url looks like https://oauth.vk.com/authorize?client_id={clientId}&scope={scope}&redirect_uri=https://oauth.vk.com/blank.html&display={displayType}&response_type=token, Url with received accessToken looks like https://oauth.vk.com/blank.html#access_token={accessToken}&expires_in={expiresIn}&user_id={userId}, note the number sign instead on question mark.
code in main form:
var authenticationForm = new AuthenticationForm();
authenticationForm.Show();
_authenticatedUser = await application.ClientAuthenticator.Authenticate(authenticationForm.GetToken);
authenticationForm.Close();
var httpClient = new HttpClient();
var request = "https://api.vk.com/method/users.get.xml?user_ids=1&fields=online";
var response = await httpClient.GetAsync(request);
authenticationForm class code:
public partial class AuthenticationForm : Form
{
private readonly TaskCompletionSource<VkAccessToken> _tokenCompletitionSource = new TaskCompletionSource<VkAccessToken>();
private Uri _redirectUri;
public AuthenticationForm()
{
InitializeComponent();
}
public async Task<IVkAccessToken> GetToken(Uri authUri, Uri redirectUri)
{
authenticationBrowser.Navigate(authUri);
_redirectUri = redirectUri;
var token = await _tokenCompletitionSource.Task;
return token;
}
private async void authenticationBrowser_Navigated(object sender, WebBrowserNavigatedEventArgs e)
{
if (!(_redirectUri.IsBaseOf(e.Url) && _redirectUri.AbsolutePath.Equals(e.Url.AbsolutePath))) return;
//working with e.Url to achieve token, userId and expiresIn, creating token variable based on them
_tokenCompletitionSource.SetResult(token);
}
}
ClientAuthenticator.Authenticate code:
public async Task<IVkAuthenticatedUser> Authenticate(Func<Uri, Uri, Task<IVkAuthenticatedUser>> aunthenticationResultGetter)
{
var authorizationUri =
new Uri("https://oauth.vk.com/authorize?client_id={clientId}&scope={scope}&redirect_uri=https://oauth.vk.com/blank.html&display=page&response_type=token");
var token = await aunthenticationResultGetter(authorizationUri, _application.Settings.RedirectUri);
//...
return newUserBasedOnToken;
}
after stepping out(using debugger) var response = await httpClient.GetAsync(request); line from main form, my system browser opens link like https://oauth.vk.com/blank.html#access_token={accessToken}&expires_in={expiresIn}&user_id={userId} - #access_token={accessToken}&expires_in={expiresIn}&user_id={userId} with recent accessToken, expiresIn and userId values. Yes, with ... - #access_token=.... in url.
I have no idea why this might happen, but I am concerned that the number sign.
important addition: it only happens if the Web browser does not have information about a session or it is expired, that is, I have to enter username and password to vk's login form. if cookies contain the necessary information and it automatically redirect to the page containing token in it's url (with # sign again), everything works as expected
I am getting the following error:
(OAuthException - #2500) An active access token must be used to query information about the current user.
Using V6 of the Facebook SDK in C#.
Below is the code that I am using, and please note that I have omitted my AppID and App Secret:
public partial class Form1 : Form
{
private string accessToken;
private void getAccessTokenButton_Click(object sender, EventArgs e)
{
FacebookClient client = new FacebookClient();
dynamic result = client.Get("oauth/access_token", new
{
client_id = "OMITTED",
client_secret = "OMITTED",
grant_type = "client_credentials"
});
accessToken = result.access_token;
}
private void retrieveFirstNameButton_Click(object sender, EventArgs e)
{
FacebookClient client = new FacebookClient();
client.AccessToken = accessToken;
dynamic result = client.Get("me");
MessageBox.Show(result.first_name);
}
}
Ah! Figured out the problem. You're using an app access token to get the information about the current logged in user, which is not allowed.
You've to get the user access token to access the details about the current logged in user. The other option is that you can pass the user id instead of 'me' to get the information about a user who has given you the required permission.
dynamic result = client.Get("User_ID");
string name = result.first_name;
Another problem I detected in your code was you are using a wrong method to create a new `FacebookClient' with the existing access token. The correct way I suppose is to pass the accessToken in the constructor.
FacebookClient client = new FacebookClient("Access_Token");
Here's a complete tutorial: Working with Facebook C# SDK