IdentityServer Hybrid Flow - Access Token is null after user successful login - asp.net

I'm having problems in retrieving access token of an authenticated user. below is my configuration
ASP.NET MVC 5 Client:
OpenIdConnect
IdentityServer3 libraries
ResponseType = "code id_token"
ASP.NET Core Identity Server:
IdentityServer4 libraries
Client Config: AllowedGrantTypes =
GrantTypes.HybridAndClientCredentials,
I'm trying to get the access token in my client using this:
AuthorizationCodeReceived = async n =>
{
// use the code to get the access and refresh token
var tokenClient = new TokenClient(TokenEndpoint, "clientid", "secret");
var response = await tokenClient.RequestAuthorizationCodeAsync(n.Code, n.RedirectUri);
},
I used this reference for above implementation - https://github.com/IdentityServer/IdentityServer3/issues/2457
but the properties in the response has null values. I need the access token so that the user logged in the client can access the api. Below is another way that i'm trying to retrieve the access token:
public async Task<ActionResult> CallApiUsingUserAccessToken()
{
var user = User as ClaimsPrincipal;
var accessToken = user.FindFirst("access_token").Value;
var client = new HttpClient();
client.SetBearerToken(accessToken);
var content = await client.GetStringAsync("http://localhost:6001/api/values");
ViewBag.Json = JArray.Parse(content).ToString();
return View("json");
}
however, user.FindFirst("access_token").Value; is null. I'm thinking of migrating my MVC client to Core because I've tried the IdentityServer4 version in an asp.net core but that seems to be a big migration to my part. Thank you.
[updated]
It never occured to me that the endpoints in the IdentityServer3 differs from IDS4. I did have to change var tokenClient = new TokenClient(TokenEndpoint, "client", "secret"); to var tokenClient = new TokenClient("http://localhost:9000/connect/token", "client", "secret") since TokenEndpoint in IDS3 is http://localhost:9000/core/connect/token which the endpoint "core" does not exist in IDS4. I'm able to get the access token in this line var tokenResponse = await tokenClient.RequestAuthorizationCodeAsync(n.Code, n.RedirectUri); but after authorization, i'm still getting nullreference exception to this var accessToken = user.FindFirst("access_token").Value; line of code.

Given the IdentityServer 4 documentation on
Switching to Hybrid Flow and adding API Access back
and an example client from IdentityServer3.Samples
MVC OWIN Client (Hybrid)
you should be able to setup a working environment.
To support debugging you should always do proper response handling as shown in example below and copied from example client. Add any response errors to your question.
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = async n =>
{
// use the code to get the access and refresh token
var tokenClient = new TokenClient(
Constants.TokenEndpoint,
"mvc.owin.hybrid",
"secret");
var tokenResponse = await tokenClient.RequestAuthorizationCodeAsync(
n.Code, n.RedirectUri);
if (tokenResponse.IsError)
{
throw new Exception(tokenResponse.Error);
}
Finally I recommend to add code for all important parts of an IdentityServer3/4 based setup - because the truth is usually burried in the details.

According to these posts, https://github.com/IdentityServer/IdentityServer3/issues/2457 & https://github.com/IdentityServer/IdentityServer3/issues/2015#issuecomment-172623173, it is a good practice to not include the access token in the claims. Hence, I followed his example, https://github.com/Mich-b/IdentityServerTMLClient/blob/master/IdentityServerTMLClient/Startup.cs, in which the access token is added in the Http Session storage.

Related

Access to Sharepoint Online from Client App

Recently I came up with an issue that I have a .NET Web API which needs to connect to SharePoint Online. In the Azure AD, I have provided all permission to the AppId "AllSites.Manage.All", AllSites.Read.All etc. I used CSOM library to pass the token to the sharepoint. But once I am trying to execute query on the clientcontext received, It is throwing 401 UnAuthorized error
private async Task<ClientContext> GetClientContextWithAccessToken1(string targetUrl)
{
var accessToken = await _tokenAcquisition.GetAccessTokenForUserAsync(new string[] {"Files.ReadWrite.All", "Sites.Manage.All", "AllSites.Read"});
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var response = await httpClient.GetAsync($"https://graph.microsoft.com/beta/me");
if(response.StatusCode == HttpStatusCode.OK)
{
var content = await response.Content.ReadAsStringAsync();
}
using(ClientContext clientContext = new ClientContext(targetUrl))
{
clientContext.ExecutingWebRequest +=
delegate (object oSender, WebRequestEventArgs webRequestEventArgs)
{
webRequestEventArgs.WebRequestExecutor.RequestHeaders["Authorization"] =
"Bearer " + accessToken;
};
return clientContext;
}
}
For the request to query https://graph.microsoft.com/beta/me , one of the below permissions is required to be granted consent from admin. like Directory.Read.All , User.Read.All ...
Also please make sure to add Sites.Read.All or Sites.ReadWrite.All Application permission in your registrated AAD Application and do admin consent for it before you getting token to access sharepoint sites.
If you're using v2 endpoint, please go to below URL in your internet browser to do admin grant:
https://login.microsoftonline.com/{yourtenant}/adminconsent?client_id={ applicationid /clientId }&state=123&redirect_uri={redirect uri of your app}
and sign in with Global administrator account and accept this permission.
Reference:
azure-app-cannot-access-sharepoint-online-sites
If you are calling Microsoft Graph API endpoints you should avoid using csom.
AllSites.Manage.All, AllSites.Read.All etc. permissions are related to SharePoint and CSOM and they will not work for Graph API endpoints.
For Graph API you need to acquire different token or better option is to use Microsoft Graph Client Library for .NET in your case.

Header token authorization not working for get request in xamarin forms

I'm working on a project where i need to get the list of users from a restful server. However, My code isn't working as I'm not getting the intended result. The rest server uses a JWT token which means i need to be authorized before i can make a request. Please how do i do this in xamarin forms.
below is my code:
{
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
"Bearer", accessToken);
var json = await client.GetStringAsync(ConstantsValue.BaseAddress + "Users");
var users = JsonConvert.DeserializeObject<List<AddedUsers>>(json);
return users;
}```

Asp.Net Web Api 2 - How to consume JWT access token and user claims using Identity Model

I have implemented an authorization server in a Asp.Net Web Api project as explained in this article.
Now I need to consume the service from a .Net c# client. In the IdentityModel documentation I can see below example:
var client = new TokenClient(
"https://server/token",
"client_id",
"secret");
var response = await client.RequestClientCredentialsAsync("scope");
var token = response.AccessToken;
Questions:
What is the purpose of having client Id and a client secret?
How a user will be authenticated using user credentials?
How can I access the user claims in client side?
What is Scope and what is the use of it?
By using IdentityModel.Client; the token can be consumed in following way.
var client = new TokenClient(authenticationUrl);
client.Timeout = TimeSpan.FromSeconds(60);
var tokenResponse = await client.RequestResourceOwnerPasswordAsync(userName, password);
var handler = new JwtSecurityTokenHandler();
var token = handler.ReadJwtToken(tokenResponse.AccessToken);
in the token itself contains claim properties.

Azure MobileServiceClient fails to authenticate user in ASP.NET

I'm having troubles with OAuth .NET backend authentication for Azure mobile-services in ASP.NET 5.0. I'm trying to implement external login with Facebook,Twitter,Google and Microsoft.
I'm successfully getting access_token from all external sources and then trying to log in into MobileServiceClient.
here is my code
var app = System.Web.HttpContext.Current.Items["AzureClient"] as MobileServiceClient;
app.Logout();
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
var accesToken = loginInfo.ExternalIdentity.Claims.FirstOrDefault(c => c.Type == "access_token");
MobileServiceUser user = null;
if (providerName == "Microsoft")
{
user = await app.LoginWithMicrosoftAccountAsync(accessToken);
}
else
{
var token = new JObject();
token.Add("access_token", accessToken);
user = await app.LoginAsync(loginInfo.Login.LoginProvider, token);
}
And I'm getting authenticated but only with facebook token. Microsoft and Google throw 401 unauthorized exception. Twitter throws "Method not allowed". What am I dowing wrong?
I've double-checked that app secret and app keys are populated for all providers in azure management portal.
Please, help
I'm not sure if tokens from social network can be forwarded to MobileServiceClient or not but it works with facebook and doesn't work with all the others. I'm really puzzled about this behaviour;
I finally ended up with creating an ActiveDirectory application and using ADAL AcquireToken method to obtain AD token for my MobileServicesClient. As it is described here
Azure Website Single Sign On accessing Azure Mobile Service from Azure Active Directory as User
here is my Method obtaining token from AD
private string GetAdToken()
{
string clientID = "<clientId>";
string authority = "<AuthorityUrl>";
string resourceURI = "<WebApiUrl>";
var appKey = "<applicationKey>";
var ac = new Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext(authority);
var clientCredential = new ClientCredential(clientID, appKey);
var ar = ac.AcquireToken(resourceURI, clientCredential);
Session["token"] = ar.AccessToken;
return ar.AccessToken;
}
and here is my method which is run before quering Azure datatables through MobileServiceClient.
private async Task<MobileServiceUser> EnsureLogin()
{
var app = System.Web.HttpContext.Current.Items["AzureClient"] as MobileServiceClient;
app.Logout();
JObject token = new JObject();
token["access_token"] = Session["token"].ToString();
return await app.LoginAsync(MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory, token);
}
So now it doesn't metter what provider I use to log in to my web application. MobileServiceClient always works with ad token.
I'm not sure if it is an acceptable practice but it works and maybe this will help somebody like me struggling against azure authentication

Testing a Web App Protected by Passive Federated Auth

My team has an ASP.NET MVC-based website and WebAPI that is protected by passive federated authentication. It all works properly. The problem we're having is that we need to test the website and the web API after an automated deployment. How can we authenticate and get the FEDAUTH cookie to the website from automated test code, assuming that the test code is run by a user authorized to access the website?
You can have your Web API support active authentication. It requires some work to change the configuration and authentication handler, but it will make your web API easily accessible from a program client as well. If you just want to get a FEDAUTH cookie in your automated test code, the following code sample can work. It mimics a browser to post the user token to the website and get a cookie back.
// The code needs the STS server and the website url
var stsUrl = "https://your_STS";
var serviceUrl = "https://your_Service";
// Use Windows Credential to get the token
var binding = new WSHttpBinding(SecurityMode.Transport);
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
var factory = new WSTrustChannelFactory(binding, stsUrl) { TrustVersion = TrustVersion.WSTrust13 };
// Override current login user credential if needed:
// factory.Credentials.Windows.ClientCredential = userCredential;
var rst = new RequestSecurityToken
{
RequestType = RequestTypes.Issue,
KeyType = KeyTypes.Bearer,
AppliesTo = new EndpointReference(serviceUrl)
};
RequestSecurityTokenResponse rstr;
var token = factory.CreateChannel().Issue(rst, out rstr);
var fedSerializer = new System.IdentityModel.Services.WSFederationSerializer();
var rstrContent = fedSerializer.GetResponseAsString(rstr, new WSTrustSerializationContext());
// After this the security token is acquired and saved in rstrContent
var client = new HttpClient();
// Initiate a request to the service, which will be redirected to STS. Read WS fed fields from redirected URL.
var response = client.GetAsync(serviceUrl).Result;
response.EnsureSuccessStatusCode();
var redirectQuery = response.RequestMessage.RequestUri.Query;
var queryParams = System.Web.HttpUtility.ParseQueryString(redirectQuery);
// construct a authentication form
var formData = new Dictionary<string, string>
{
{"wa", queryParams["wa"]},
{"wresult", rstrContent},
{"wctx", queryParams["wctx"] },
};
// post the authentication form to the website.
response = client.PostAsync(serviceUrl, new FormUrlEncodedContent(formData)).Result;
response.EnsureSuccessStatusCode();
// After this, the auth cookie is set in this HttpClient that you can use to access your service

Resources