instaSharp oauthResponse Not work - c#

I want to use instaSharp to use instagram api , get followers anp posts and ...
when I get code with callbackUrl I cant Send Requesttoken(code)
and my oauthResponse is null ...
this is my code :
async Task getaouth()
{
var clientId = ConfigurationManager.AppSettings["client_id"];
var clientSecret = ConfigurationManager.AppSettings["client_secret"];
var redirectUri = ConfigurationManager.AppSettings["redirect_uri"];
var realtimeUri = "";
InstagramConfig config = new InstagramConfig(clientId, clientSecret, redirectUri, realtimeUri);
Session.Add("InstaSharp.config", config);
// add this code to the auth object
var auth = new InstaSharp.OAuth(config);
// now we have to call back to instagram and include the code they gave us
// along with our client secret
var oauthResponse = await auth.RequestToken(code);
// tell the session that we are authenticated
//config.isAuthenticated = true;
Response.Write(r.ToString());
// both the client secret and the token are considered sensitive data, so we won't be
// sending them back to the browser. we'll only store them temporarily. If a user's session times
// out, they will have to click on the authenticate button again - sorry bout yer luck.
Session.Add("InstaSharp.AuthInfo", oauthResponse);
// all done, lets redirect to the home controller which will send some intial data to the app
//return RedirectToAction("Index", "Home");
Response.Write("");
}
after this my oauthResponse is null !
and after call this method
_users.GetSelf()
i get it :
An exception of type 'System.InvalidOperationException' occurred in InstaSharp.dll but was not handled in user code
Additional information: You are not authenticated

Have you already registered your test client application on your account at Instagram developers?
If you haven't, sing in with your account here, click on top-button "Manager clients" and add your test application to get the correct client ID and client secret informations.

Use this way,
Install latest InstaSharp and just do this:
private InstagramConfig _config;
public async Task< ActionResult> somename(string code)
{
if (code != null)
{
_config = new InstagramConfig(["InstgramClientId"],
["InstgramClientSecret"],
["InstgramRedirectUrl"]
);
var instasharp = new InstaSharp.OAuth(_config);
var authInfo = await instasharp.RequestToken(code);
var user = new InstaSharp.Endpoints.Users(_config, authInfo);
ViewBag.Username = user.OAuthResponse.User.Username;
ViewBag.Token = authInfo.AccessToken;
return View();
}
return View("name");
}
In your case, you have to make call to your method like:
var authresponse = await getaouth();
Make sure your calling function is async task.

Related

ASP.NET Identity - Get Saved third-party access tokens

I have an app that will operate almost entirely on Spotify OAuth, that will have features to alter the playback of your music.
I'm able to get Spotify OAuth working perfectly such that I can log into my app, but after I've logged in, I need access to the current user's spotify access_token so that I can forward it to my spotify requests.
I followed this guide from ms to try to save the tokens: https://learn.microsoft.com/en-us/aspnet/core/security/authentication/social/?view=aspnetcore-6.0&tabs=visual-studio
And I have tried all these ways to then save that token into the HttpContext such that I can access it:
options.Events.OnCreatingTicket = ctx =>
{
List<AuthenticationToken> tokens = ctx.Properties.GetTokens().ToList();
tokens.Add(new AuthenticationToken()
{
Name = "TicketCreated",
Value = DateTime.UtcNow.ToString()
});
var spotifyAccessToken = tokens.FirstOrDefault(x => x.Name == "access_token").Value;
tokens.Add(new AuthenticationToken()
{
Name = "SpofityAccessToken",
Value = spotifyAccessToken
});
//store all the tokens as directed by MS
ctx.Properties.StoreTokens(tokens);
//store the properties into the HttpContext in 2 different ways
ctx.HttpContext.Items["Properties"] = ctx.Properties;
ctx.HttpContext.Features.Set(ctx.Properties);
//try adding a claim to the user
ctx.Identity.AddClaims(new[] { new Claim("SpotifyAccessToken", spotifyAccessToken) });
return Task.CompletedTask;
};
The problem I'm having is how do I then get this token out? all of these methods are not working:
[HttpGet]
public async Task Get()
{
await HttpContext.SignInAsync(User);
// try to re-run Authenticate, even though i'm already in an [Authorize] controller
var res = await HttpContext.AuthenticateAsync();
//props2 does not have the tokens i set
var props2 = res.Properties;
//props comes back null
var props = HttpContext.Features.Get<AuthenticationProperties>();
//claims has no SpotifyAccessToken claim
var claims = User.Claims.ToList();
var token = "hard-coded";
//here is where i need the token to then query spotify
var client = new SpotifyAPI.Web.SpotifyClient(token);
var res2 = await client.Player.GetCurrentPlayback();
}
I feel like I've tried everything, what am i doing wrong?
This is in a .NET 6 blazor wasm, .net core hosted app.
Also tried the solutions here to no avail Get AuthenticationProperties in current HttpRequest after HttpContext.SignInAsync
signInManager.UpdateExternalAuthenticationTokensAsync adds the the authentication tokens in [dbo].[AspNetUserTokens]
External login is where I call it:
// Sign in the user with this external login provider if the user already has a login.
var signInResult = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: true, bypassTwoFactor: true);
if (signInResult.Succeeded)
{
await _signInManager.UpdateExternalAuthenticationTokensAsync(info);
_logger.LogInformation("{Name} logged in with {LoginProvider} provider.", info.Principal.Identity.Name, info.LoginProvider);
return LocalRedirect(returnUrl);
}
Later on you can get it by using :
var token = await userManager
.GetAuthenticationTokenAsync(user, "Spotify", "access_token");
var expiresAtStr = await userManager
.GetAuthenticationTokenAsync(user, "Spotify", "expires_at");
If the token is stored in the Cookie then you can access the various tokens using:
string accessToken = await HttpContext.GetTokenAsync("access_token");
string idToken = await HttpContext.GetTokenAsync("id_token");
string refreshToken = await HttpContext.GetTokenAsync("refresh_token");
string tokenType = await HttpContext.GetTokenAsync("token_type");
string accessTokenExpire = await HttpContext.GetTokenAsync("expires_at");
However, you can not store data in ctx.HttpContext and assume it will be persisted across requests. either you sign-in the user using the cookie middleware or you store the tokens in the UserSession object.
See this article on how to configure and store data in the session, that will be persisted across requests.
Session and state management in ASP.NET Core
If you configure it properly, then you can use it like:
HttpContext.Session.SetString("token", token.Trim());

Is it possible use AuthorizeAttribute with a external api call?

i'm working with asp mvc 5, and i have to schedule some tasks, so i want to create a simple methods to be called from a simple console program C# and schedule them with Windows Task Scheduler.
The think is, i'm using Identity with Authorize attribute to manage the user permissions.
For example, i have the next method:
[Authorize(Roles="Admin")]
public async Task<JsonResult> CriticalTask(string someParam)
{
//procesing data
return null;
}
The think, is:
I dont know how can i do the login to pass the validation from the Authorize(Roles="Admin")
I try creating a simple method to login before it, but that doesn't work
I'm trying some like this
const string URL = "http://localhost:53665/";
RestClient mClient = new RestClient(URL);
const string parameterUserName = "userName";
const string parameterPassword = "password";
const string ruta = "Usuarios/ApiLogin";
var request = new RestRequest(ruta);
request.AddParameter(parameterUserName, "userName");
request.AddParameter(parameterPassword, "password");
//Method to login
var result2 = mClient.Execute(request);
Console.WriteLine($"Login\n{result2.Content}");
//Method that needs Admin permissions
request = new RestRequest("Usuarios/Test");
var result3 = mClient.Execute(request);
Console.WriteLine($"Test\n{result3.Content}");
is that possible only with Authorize attribute? or i need to implement some token method to authorize this calls?
Thanks!
What you should do is to save the received token after login and then add the token to request header which needs to be authorized:
var result = mClient.Execute(request);
string resultContent = result.Content.ReadAsStringAsync().Result;
//This token will be used for authorization
var token = JsonConvert.DeserializeObject<TokenModel>(resultContent);
var request = new RestRequest("Usuarios/Test"); //add token to header of request
mClient.AddDefaultHeader("Authorization", string.Format("bearer {0}", token.Access_Token));
var result3 = mClient.Execute(request);
Token model:
public class TokenModel
{
...
public string Access_Token { get; set; }
...
}
The easiest solution would be to use BasicAuth - you pass Credentials in headers for each request, and each request is validated separately - search for MVC Basic auth fr sample setup. I's the easiest form - but also very insecure as you pass your credentials in each call in almost plain text (it is only base64 of your credentials)
I'd suggest you to use Identity Server 4 to authorize your client using bearer token.
this way before first call you request token from server and then pass it to following requests and use this to authorize your api calls.
see following tutorial about setup.
http://docs.identityserver.io/en/aspnetcore1/quickstarts/6_aspnet_identity.html
in following url you can see example of in memory users, but also token requests.it's easy but obsolete
https://neelbhatt.com/2018/03/04/step-by-step-setup-for-the-auth-server-and-the-client-identityserver4-with-net-core-part-ii/
you can also use some sort of following code to obtain token in less obsolete way:
using (var httpClient = new HttpClient()) {
var discovery = await _httpClient.GetDiscoveryDocumentAsync(_configuration["ApiBaseAddress"]);
if (discovery.IsError)
{
return false;
}
request.Address = discovery.TokenEndpoint;
request.ClientId = _configuration["AuthClientName"];
request.ClientSecret = _configuration["AuthClientSecret"];
var request = new PasswordTokenRequest
{
UserName = "yourUserName",
Password = "yourPassword"
};
var token = await _httpClient.RequestPasswordTokenAsync(request);
}
in token.AccessToken you have your access token - the one needed to be sent to call api. you also have your refresh token in token.RefreshToken - it will be useful later
then to send call simply add bearer token to your HttpRequestMessage and it's done
var _httpClient = new HttpClient();
//Configure your http client here
var req= new HttpRequestMessage("http request method", "request uri");
req.SetBearerToken("your access token goes here);
var result = await _httpClient.SendAsync(req);
keep in mind that after you receive permission denied it's better to refresh token than obtain another one (you don't need to send your credentials). You use your refresh token i mentioned earlier.
The code for refreshing token is very similar to obtaining token by login/password. Just instead of PasswordTokenRequest class use following class:
var request = new RefreshTokenRequest
{
RefreshToken = _refreshToken
};
and instead httpClient.RequestPasswordTokenAsync(request) use httpClient.RequestRefreshTokenAsync(request). rest of code may remain similar.
I end up creating a custom attribute, based on the reply posted by crrlos in StackOverflow in Spanish.
I will translate it as best I can so that it can serve others
What you can do is create a custom authorization attribute, for that
you create a class that inherits AuthorizeAttribute and override the
AuthorizeCore method. The modification consists in passing to the
route an additional parameter to indicate that it is calling from the
task scheduler, if the parameter is not found then it will perform the
normal validation, if the parameter is found (it must have a value or
if it will not be null ) Then take the credentials of the url and
perform the validation, if they are correct return a true allowing
access to the method.
public class CustomAuthorization : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
//get special parameter indicating that the request was made from the task scheduler
var parametro = httpContext.Request.QueryString.Get("parametro_especial");
if(parametro != null)
{
// get access credentials and validate them
// if they are valid, return true
}
//if they are not valid, or aren't present
//try with deffault validate.
return base.AuthorizeCore(httpContext);
}
}
How to use it?
[CustomAuthorization (Roles = "Admin")]
public JsonResult CargarTodosLosArticulos()
{
return null;
}

Web Application and API AzureAD authentication flow ASP.NET Core

I'm currently confused about how to realize the authentication / authorization flow.
I'm developing two applications, the one is the frontend/Webapplication and the other the backend/API, both with ASP.NET Core. The goal is to use the AzureAD and use the users/groups from the domain. The authentication I already implemented on both applications and I'm able to login and restrict content based on the login state.
As reference I took this example from a microsoft developer. There should be exactly this what I want to do. There is a WebApp and API. The used authentication flow is the authorization code flow. First the user needs to login and after that when some data needs to be requested from the API, an access token will be requested.
Question 1: Is this the right authentication flow? For me this seems like a doubled authentication, because first I authenticate myself at the frontend and when the Webapp needs some data I need to authenticate myself again at the backend. The same Azure AD tenant is used, so what do you think here?
The next point what seems very "ugly" is the procedure getting some data. In the example when some data is requested first the token will be requested and after this the data. But in my opinion with a lot of boilerplate. The example code below is needed for just one request of all ToDo items.
// GET: /<controller>/
public async Task<IActionResult> Index()
{
AuthenticationResult result = null;
List<TodoItem> itemList = new List<TodoItem>();
try
{
// Because we signed-in already in the WebApp, the userObjectId is know
string userObjectID = (User.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier"))?.Value;
// Using ADAL.Net, get a bearer token to access the TodoListService
AuthenticationContext authContext = new AuthenticationContext(AzureAdOptions.Settings.Authority, new NaiveSessionCache(userObjectID, HttpContext.Session));
ClientCredential credential = new ClientCredential(AzureAdOptions.Settings.ClientId, AzureAdOptions.Settings.ClientSecret);
result = await authContext.AcquireTokenSilentAsync(AzureAdOptions.Settings.TodoListResourceId, credential, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));
// Retrieve the user's To Do List.
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, AzureAdOptions.Settings.TodoListBaseAddress + "/api/todolist");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
HttpResponseMessage response = await client.SendAsync(request);
// Return the To Do List in the view.
if (response.IsSuccessStatusCode)
{
List<Dictionary<String, String>> responseElements = new List<Dictionary<String, String>>();
JsonSerializerSettings settings = new JsonSerializerSettings();
String responseString = await response.Content.ReadAsStringAsync();
responseElements = JsonConvert.DeserializeObject<List<Dictionary<String, String>>>(responseString, settings);
foreach (Dictionary<String, String> responseElement in responseElements)
{
TodoItem newItem = new TodoItem();
newItem.Title = responseElement["title"];
newItem.Owner = responseElement["owner"];
itemList.Add(newItem);
}
return View(itemList);
}
//
// If the call failed with access denied, then drop the current access token from the cache,
// and show the user an error indicating they might need to sign-in again.
//
if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
return ProcessUnauthorized(itemList, authContext);
}
}
catch (Exception)
{
if (HttpContext.Request.Query["reauth"] == "True")
{
//
// Send an OpenID Connect sign-in request to get a new set of tokens.
// If the user still has a valid session with Azure AD, they will not be prompted for their credentials.
// The OpenID Connect middleware will return to this controller after the sign-in response has been handled.
//
return new ChallengeResult(OpenIdConnectDefaults.AuthenticationScheme);
}
//
// The user needs to re-authorize. Show them a message to that effect.
//
TodoItem newItem = new TodoItem();
newItem.Title = "(Sign-in required to view to do list.)";
itemList.Add(newItem);
ViewBag.ErrorMessage = "AuthorizationRequired";
return View(itemList);
}
//
// If the call failed for any other reason, show the user an error.
//
return View("Error");
}
Question 2: Is there a "less ugly" approach to access the data if the flow in Q1 is right?
I found a proper solution to solve this.
I just used the approach from this example here multitenant-saas-guidance and it works like charm.

How to handle Stormpath ID Site JWT response

I'm trying to create an ASP.NET application with Stormpath ID Site authorization. I create request and response action and successfully got the account.
But what next? How to tell the application that the user is authenticated?
public async Task<RedirectResult> Callback(string jwtResponse)
{
var client = Request.GetStormpathClient();
var app = await client.GetApplicationAsync(appUrl);
var requestDescriptor = HttpRequests.NewRequestDescriptor()
.WithMethod("GET")
.WithUri("http://localhost:50084/Auth/Callback?jwtResponse=" + jwtResponse)
.Build();
var idSiteListener = app.NewIdSiteAsyncCallbackHandler(requestDescriptor);
var accountResult = await idSiteListener.GetAccountResultAsync();
var account = accountResult.GetAccountAsync().Result; //Account
//What I must do here to tell application that user is authenticated
return Redirect("/");
}
Instead of getting the account from the ID Site response, you could exchange the JWT for a Stormpath access token:
public async Task<RedirectResult> Callback(string jwtResponse)
{
var client = Request.GetStormpathClient();
var app = await client.GetApplicationAsync(appUrl);
var exchangeRequest = new StormpathTokenGrantRequest
{
Token = jwtResponse
});
var grantResponse = await application.ExecuteOauthRequestAsync(exchangeRequest);
// Return grantResponse.AccessTokenString in a secure HTTPOnly cookie, or as a JSON response
}
If you use the Stormpath ASP.NET plugin, you can enable ID Site and this will be handled for you automatically.
Disclaimer: I'm the package author.

How to implement refresh token workflow into OAUTH workflow in MVC C# app?

I am new to OAUTH. I have been working on implementing OAUTH into my MVC c# application to access ping federate. After much research, and failed attempt at using the ping federate nuget, I came across this link that finally gave some clarity to the full process with a coding example. I have came across much generic examples of the endpoints i need to access but never a full workflow coding example. After implementing that code with some changes and was successful at signing in the ping user into my MVC app, I started doing more research about the refresh token. Questions...
Q. I know how to access a a refresh token, meaning I know which endpoint used to refresh the access token after I have authenticated the user in ping federate. But what is the refresh token used for? Is it used to extend my application's session once it ends? Or it used for if the user signs out of my application then they click the 'Sign in with Ping Federate' link on the login and not have them authenticate again as long as the refresh token is still valid?
Q. And if the refresh token is used for when after a user authenticates the first time, and I save the refresh token in the db and then user signs back using that 'Sign in with Ping Federate' link on my login back how can I know what user that is to lookup the refresh token in the db to give them access to my site without re-authenticating them with ping federate? Since when they come to that link 'Sign in with Ping Federate' I do not know who they are?
This is the below code that I am using, from user MatthiasRamp in the link i provided...I want to add my refresh token logic with the below code.
public async Task<ActionResult> Login(string returnUrl)
{
if (string.IsNullOrEmpty(returnUrl) && Request.UrlReferrer != null)
returnUrl = Server.UrlEncode(Request.UrlReferrer.PathAndQuery);
if (Url.IsLocalUrl(returnUrl) && !string.IsNullOrEmpty(returnUrl))
_returnUrl = returnUrl;
//callback function
_redirectUrl = Url.Action("AuthorizationCodeCallback", "ExternalLogin", null, Request.Url.Scheme);
Dictionary<string, string> authorizeArgs = null;
authorizeArgs = new Dictionary<string, string>
{
{"client_id", "0123456789"}
,{"response_type", "code"}
,{"scope", "read"}
,{"redirect_uri", _redirectUrl}
// optional: state
};
var content = new FormUrlEncodedContent(authorizeArgs);
var contentAsString = await content.ReadAsStringAsync();
return Redirect("http://localhost:64426/oauth/authorize?" + contentAsString);}
public async Task<ActionResult> AuthorizationCodeCallback()
{
// received authorization code from authorization server
string[] codes = Request.Params.GetValues("code");
var authorizationCode = "";
if (codes.Length > 0)
authorizationCode = codes[0];
// exchange authorization code at authorization server for an access and refresh token
Dictionary<string, string> post = null;
post = new Dictionary<string, string>
{
{"client_id", "0123456789"}
,{"client_secret", "ClientSecret"}
,{"grant_type", "authorization_code"}
,{"code", authorizationCode}
,{"redirect_uri", _redirectUrl}
};
var client = new HttpClient();
var postContent = new FormUrlEncodedContent(post);
var response = await client.PostAsync("http://localhost:64426/token", postContent);
var content = await response.Content.ReadAsStringAsync();
// received tokens from authorization server
var json = JObject.Parse(content);
_accessToken = json["access_token"].ToString();
_authorizationScheme = json["token_type"].ToString();
_expiresIn = json["expires_in"].ToString();
if (json["refresh_token"] != null)
_refreshToken = json["refresh_token"].ToString();
//SignIn with Token, SignOut and create new identity for SignIn
Request.Headers.Add("Authorization", _authorizationScheme + " " + _accessToken);
var ctx = Request.GetOwinContext();
var authenticateResult = await ctx.Authentication.AuthenticateAsync(DefaultAuthenticationTypes.ExternalBearer);
ctx.Authentication.SignOut(DefaultAuthenticationTypes.ExternalBearer);
var applicationCookieIdentity = new ClaimsIdentity(authenticateResult.Identity.Claims, DefaultAuthenticationTypes.ApplicationCookie);
ctx.Authentication.SignIn(applicationCookieIdentity);
var ctxUser = ctx.Authentication.User;
var user = Request.RequestContext.HttpContext.User;
//redirect back to the view which required authentication
string decodedUrl = "";
if (!string.IsNullOrEmpty(_returnUrl))
decodedUrl = Server.UrlDecode(_returnUrl);
if (Url.IsLocalUrl(decodedUrl))
return Redirect(decodedUrl);
else
return RedirectToAction("Index", "Home");
}

Categories