I have followed the wonderful tutorial by Taiser Joudah for implementing refresh tokens with Asp.Net Web Api 2 and Owin. It all went so well...except I can't get it to work. :-) It all "seems" like it works up until the point I request a refresh token. Then all I get back is:
“error”: “invalid_grant”
and no description to go with. One of the comments on that post had the same symptom and the response was to generate a MachineKey for the web.config. I tried this but it didn't help. And I'm thinking that maybe only applies when the Auth and Resource server are not the same anyway, which in this case they are.
The bottom line is using PostMan I can make the request for the refresh token and in ReceiveAsync the context.Ticket deserialization does not work. After the call to “context.DeserializeTicket(refreshToken.ProtectedTicket);” the context.Ticket is still null. Curiously if I manually deserialize the ProtectedTicket using the Acccess Token’s AccessTokenFormat it will deserialize properly. But it does not work using the Refresh Token's RefreshTokenFormat object:
var thisWorks = Startup.OAuthOptions.AccessTokenFormat.Unprotect(refreshToken.ProtectedTicket);
var thisDoesnt = Startup.OAuthOptions.RefreshTokenFormat.Unprotect(refreshToken.ProtectedTicket);
It sure seems like a config problem…but I’ve racked my brains and compared a lot of samples. What could be causing this?
EDIT
Oops...meant to link to the tutorial article: http://bitoftech.net/2014/07/16/enable-oauth-refresh-tokens-angularjs-app-using-asp-net-web-api-2-owin/
I think your problem is the order you create the refresh_token with CreateAsync.
Make sure you follow order bellow:
context.Ticket.Properties.IssuedUtc = refreshTokenProperties.IssuedUtc;
context.Ticket.Properties.ExpiresUtc = refreshTokenProperties.ExpiresUtc;
context.SetToken(context.SerializeTicket());
Then you can persist the token into DB. But have in mind that it is not necessary to persist. You can DeserializeTicket from context.Token in ReceiveAsync:
public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
context.DeserializeTicket(context.Token);
if (context.Ticket != null)
{
context.SetTicket(context.Ticket);
}
}
Hope this help!
Following the same tutorial, I had the same problem, this worked for me
public async Task CreateAsync(AuthenticationTokenCreateContext context)
{
//...
context.Ticket.Properties.AllowRefresh = true;
token.ProtectedTicket = context.SerializeTicket();
//...
}
Related
I am new to JWT with basic idea of how it works. I have set the jwt token inside cookie from my web api.
Response.Cookies.Append("X-Access-Token", foundUser.Token
, new CookieOptions { HttpOnly = true }) ;
Now i am trying to call a web api get request which is marked as authorised from my agular application.
But inside angular i dont have a way to send the cookie. As per few documents i came to know that http only cookies are sent directly with our interference but i am not able to fetch the data with unauthorised error, which means that the token is not being used. I have not included the same in the get method as well. see below.
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[HttpGet]
public async Task<ActionResult<IEnumerable<Invoice>>> GetInvoices([FromQuery]QueryParameters queryParameters)
{
return await _uOW.InvoiceRepository.GetInvoices(queryParameters);
}
Do i have to add some logic to read the cookie in backend? I didnt find any answer for this.
Please let me know if there is something missing.
Also inside angular i have not written any special code for calling this get. Is that required?
var headerForToken = new HttpHeaders({'Authorization':`Bearer ${sessionStorage.getItem("token")}`});
return this.http.get<any>(this.APIUrl+'Invoices/GetInvoices',{headers:headerForToken });
This is my current approach in which i am using local storage, but i really need to use the http only cookie.
If there is any link for solution or anything that would be really helpfull
Update 1: So we need to add a domain for this. I tried adding domain still it is not visible when i try to see the cookies.
Response.Cookies.Append("X-Access-Token", foundUser.Token
, new CookieOptions { HttpOnly = true, Domain = ".localhost:4200" }) ;
I've tried different ways to connect the Microsoft sign in function which open a webpage so you can use things like sign in with MFA. I manage to get this to work in Postman and now im trying it in C# particularly in .NET MVC 5.
HomeController:
public ActionResult TestAuth()
{
HttpClient client = new HttpClient();
var bodyParams = new Dictionary<string, string>();
bodyParams.Add("client_id", "{my_client_id}");
bodyParams.Add("client_secret", "{my_client_secret}");
bodyParams.Add("scope", "openid");
bodyParams.Add("redirect_uri", "https://localhost");
bodyParams.Add("grant_type", "authorization_code");
var response = client.PostAsync("https://login.microsoftonline.com/{my_tenant_id}/oauth2/v2.0/authorize", new FormUrlEncodedContent(bodyParams)).Result;
return View("TestAuth", new { response.Content.ReadAsStringAsync().Result });
}
View TestAuth.cshtml:
#model dynamic
#Html.Raw(Model)
If i sign in with my email on that domain, or any text at all really, i get this message. I cannot see why this issue occurs it gives me zero information what to do next more than just trying until you make it basically :). I've looked at tons of different Microsoft documentations, Stack posts, forums etc but with no success.
The postman call example:
Is it possible I'm doing something wrong in the request in the c# code or am i missing something important like configurations in Azure AD etc?
I'm up for anything that will work that i can sign into a Microsoft account that use MFA, then i can use their login to fetch data from Microsoft Graph based on their permissions basically.
P.S. I also can fetch data with the access token generated from postman so it's working as expected. I only need to "convert the postman call to c#" to make it work esentially. Any help is appreciated :)
You’re trying to do an oauth2 request from the controller. The request you’re sending is incorrect.
Microsoft made a great sample on how to use the Microsoft identity platform in a dotnet application https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/tree/master/1-WebApp-OIDC
In a nutshell you redirect the user to the endpoint (so not a get/post from the controller, but actually a redirect 302 response to the token url).
The user then has to login and is redirected to the webapplication.
Your webapplication will get an authorization code that is has to exchange for an access token by a post request.
Postman does this for you, but in order to do it in dotnet core, just follow the sample.
I didn't find a soultion to this specific problem what i did find was another guide which led me to this github project https://github.com/Azure-Samples/ms-identity-aspnet-webapp-openidconnect
Which had similar code in the Startup.cs file but actually had some examples like SendMail and ReadMail etc which was fetched from ms graph api. This gave me some idea of how this project was structured compared to mine. So one thing that was missing was this part I couldnt figure out:
IConfidentialClientApplication app = await MsalAppBuilder.BuildConfidentialClientApplication();
var account = await app.GetAccountAsync(ClaimsPrincipal.Current.GetAccountId());
So the Msal app builder which is a custom made thingy i needed to get the current user etc which i needed. This works fine and after that i can start doing requests to the graph api like adding scopes etc and make http request.
Example see groups:
[Authorize]
[HttpGet]
public async Task<ActionResult> GetGroups()
{
IConfidentialClientApplication app = await MsalAppBuilder.BuildConfidentialClientApplication();
var account = await app.GetAccountAsync(ClaimsPrincipal.Current.GetAccountId());
string[] scopes = { "GroupMember.Read.All", "Group.Read.All", "Group.ReadWrite.All", "Directory.Read.All", "Directory.AccessAsUser.All", "Directory.ReadWrite.All" };
AuthenticationResult result = null;
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0/groups");
try
{
//Get acccess token before sending request
result = await app.AcquireTokenSilent(scopes, account).ExecuteAsync().ConfigureAwait(false);
if (result != null)
{
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
//Request to get groups
HttpResponseMessage response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
ViewBag.Groups= response.Content.ReadAsStringAsync().Result;
return View("MyView");
}
}
}
catch (Exception ex)
{
Response.Write($"Error occured:{System.Environment.NewLine}{ex}");
}
return View();
}
I have a .net console application that I want to use to pull Instagram posts via the Instasharp wrapper using a hashtag search.
I use C# .net web forms extensively and am not very familiar with MVC nor how to use the await keyword. The code sample below seems to run, but never provides any output.
This line:
var tagInfo = await tagApi.Get("soccer");
Returns me to the calling method with no indication of retrieved data.
Can anyone provide insights as to what I am doing wrong here?
public static async void GetInstagram(String tag, InstagramConfig config)
{
var instagramPosts = await LoadInstagramPosts(tag, config);
dynamic dyn = JsonConvert.DeserializeObject(instagramPosts.ToString());
foreach (var data in dyn.data)
{
Console.WriteLine("{0} - {1}", data.filter, data.images.standard_resolution.url);
}
}
public static async Task<TagResponse> LoadInstagramPosts(String hashTagTerm, InstagramConfig config)
{
var tagApi = new InstaSharp.Endpoints.Tags(config);
var tagInfo = await tagApi.Get("soccer");
}
EDITED code after first comment which solved my initial problem.
I feel like I'm close but something is still missing.
See specific questions below...
I've based the code on the documentation from InstaSharp GitHub (https://github.com/InstaSharp/InstaSharp). GitHubs example is based on an MVC application, mine is not an MVC project, but a console application.
I feel like I am very close and maybe others will benefit from helping me solve this.
My specific questions...
1) Not sure where the 'code' parameter in the OAuth method originate??
2) How to perform the needed call backs with Instagram??
var config = new InstaSharp.InstagramConfig(location.InstagramClientId, location.InstagramClientSecret, "http://localhost");
string instagramLoginLink = InstagramLogin(config);
GetInstagram("soccer", config, instagramLoginLink);
public static async void GetInstagram(String tag, InstagramConfig config, string code)
{
OAuthResponse oAuthResponse = await OAuth(code, config);
var instagramPosts = await LoadInstagramPosts(tag, config, oAuthResponse);
if(instagramPosts.Data != null)
{
dynamic dyn = JsonConvert.DeserializeObject(instagramPosts.Data.ToString());
foreach (var data in dyn.data)
{
Console.WriteLine("{0} - {1}", data.filter, data.images.standard_resolution.url);
}
}
}
public static string InstagramLogin(InstagramConfig config)
{
var scopes = new List<OAuth.Scope>();
scopes.Add(InstaSharp.OAuth.Scope.Likes);
scopes.Add(InstaSharp.OAuth.Scope.Comments);
string link = InstaSharp.OAuth.AuthLink(config.OAuthUri + "authorize", config.ClientId, config.RedirectUri, scopes, InstaSharp.OAuth.ResponseType.Code);
return link;
}
public static async Task<OAuthResponse> OAuth(string code, InstagramConfig config)
{
// add this code to the auth object
var auth = new OAuth(config);
// now we have to call back to instagram and include the code they gave us
// along with our client secret
return await auth.RequestToken(code);
}
public static async Task<TagResponse> LoadInstagramPosts(String hashTagTerm, InstagramConfig config, OAuthResponse OAuth)
{
var tagApi = new InstaSharp.Endpoints.Tags(config, OAuth);
return await tagApi.Get("soccer");
}
I'm a bit late to the show, yet probably my answer will help someone who find this question when googling, someone like me.
The main problem with your approach is that Instagram is using OAuth authentication. I suggest you to google on OAuth to understand the principles, but I will try to explain the practical points of it below.
OAuth approach means that the result of the InstagramLogin method in the snippet above is not the code. It's the link where you need to send you user (yes, using a browser or a web-view) so that they can sign into their Instagram account and then authorize your application to access their data (so-called user consent screen).
In the end, after user consent, Instagram will redirect browser to the URL of your choice (it should be previously added in the list of allowed redirect urls in Instagram API -> Manage Clients (top-right corner) -> Create/Select client to Manage -> Security tab)
You can try set a breakpoint and copy the value of instagramLoginLink into your browser's address box. You will be able to see the whole flow of authentication and consent - and finally the redirect url that will most probably produce 404 in your browser.
This final link will contain the code in a get parameter. It's the so-called grant code that allows you to get an access token. This code is to be extracted from url and then used in your call to OAuth.RequestToken).
PS: Yes, everything I say above means that you need either a web app running that will redirect user to Instagram or a client-side app that will show the user a web view and somehow handle the moment when Instagram sends the user back to your redirect url - to grab the code and proceed.
in my C# application, I am trying to get aouth2 access and refresh tokens:
https://developers.google.com/accounts/docs/OAuth2InstalledApp
On the phase: Handling the Response, When I make the call I am supposed to get something like:
{
"access_token":"1/fFAGRNJru1FTz70BzhT3Zg",
"expires_in":3920,
"token_type":"Bearer",
"refresh_token":"1/xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI"
}
but I get
{
"access_token":"1/fFAGRNJru1FTz70BzhT3Zg",
"expires_in":3920,
"token_type":"Bearer",
}
Thus: refresh_token is missing. I debugged and I am sure I hit the api method: FetchAccessAndRefreshTokens but I have no refresh_token.
PS: I am using 201306 API
Any ideas?
In the API, within OAuth2ProviderForApplications.cs file, in GetAuthorizationUrl() method, on line 100 if you add &approval_prompt=force to the string:
return string.Format("{0}?scope={1}&state={2}&redirect_uri={3}&response_type={4}&" +
"client_id={5}&access_type={6}&approval_prompt=force"
it works. But this is a horrible workaround plus it might create apache license issues.
How found: in google oauth2 playground (https://developers.google.com/oauthplayground/) this parameter (approval_prompt=force) is set and if you omit it, it does not give refresh token.
Good afternoon,
I'm using version 5.4.1 of the Facebook C# SDK. I should note that I am using the source code directly rather than the DLLs (in case this makes any difference).
So the fact that filter attributes are setup is awesome (thank you dev team :).
My issue is occurring during the initial request (before I get to using verifying the GET response from Facebook)
Here is my initial request:
dynamic result = fb.Post(
string.Format("/{0}/subscriptions",
FacebookApplication.Current.AppId),
new Dictionary<string, object>
{
{"object", "user"},
{"fields", "friends"},
{
"callback_url",
"http://localhost:16917/subscription/verify"
},
{
"verify_token",
"77FB802F-1147-48F0-BB0F-E4E9BC1FBCFC"
}
});
I'm finding that an exception is internally being thrown and via Fiddler I'm seeing that the request is never going out. The exception is:
$exception {"(OAuthException) (#15) This method must be called with an app access_token."} System.Exception {Facebook.FacebookOAuthException}
I initially thought this may be related to Facebook.FacebookClient's PrepareRequest method:
if (httpMethod == HttpMethod.Get)
{
// for GET, all parameters goes as querystrings
input = null;
queryString.Append(FacebookUtils.ToJsonQueryString(parameters));
}
else
{
if (parameters.ContainsKey("access_token"))
{
queryString.AppendFormat("access_token={0}", parameters["access_token"]);
parameters.Remove("access_token");
}
}
but commenting out the line parameters.Remove("access_token"); made no difference.
Any help would be greatly appreciated!
What access_token are you using?
Are you using a User's access token, or an App access token?
If you are using an User's access token take a look here http://developers.facebook.com/docs/authentication/#app-login on how to get an App access token.
You will need to use the app access_token.
You can easily create an app access_token using the following constructor.
var fb = new FacebookClient("appid", "appsecret");
dynamic result = fb.Post( .... );