I have a basic SPA template with WebApi 2 backend that uses Microsoft account as external login provider. I also have a WP8 client in which users can login with their Microsoft account, both the SPA and WP8 client use ClientId so a users with same email address get the same identifiers in both clients (the ProviderKeys stored at the db match the id's for the same users when they log in on the phone).
When a user has logged in through the browser I can get their access_token and authenticate further requests they make against the api through the bearer token, this works well, returning 200 every time. On the WP8 client it doesnt work however, this is how I get the token:
LiveAuthClient Auth = new LiveAuthClient("0000000041422E16");
//some code removed
LiveConnectClient connect = new LiveConnectClient(Auth.Session);
string access_token = connect.Session.AccessToken;
//use token to send a http request to an authorized endpoint
I've used Fiddler to test the access token and the WebApi always return 401, what am I doing wrong here? Aren't the tokens sent back by the Live SDK client valid for authentication on the WebApi endpoints?
Related
I am able to Do Authentication and Authorization for my .netcore MVC app+reactjs (Billing App)
this application will be hosted on IIS, and on the same server planning to host the .netcore Webapi
(chart App).
By using Billing App we will call the chart WebApis. NOW WebApi should be Authenticate/Authorize user based on token sent by front-end app (token-based webapi that does Authe/Autho, no another login pop-up)
Able to Generate token using postman-see img by requesting below link
https://login.microsoftonline.com/{{tenandId}}/oauth2/token
Will sent this token with the header to Web-API, which will be having the same configuration of Azure AD app (client id, scope, etc.) as Billing App has.
Api should validate the token and send the chart data.
Should try adal/msal in reactjs so and decorate WebApis with Authorize attribute so that will take care of Authentication & Authorization?
Got many link but few code aren't working and few process is no more works for Azure, and few are having huge code and not what exactly I am looking for.
Basically I'll host one App in .netcore that does Auth part, now the WebApi should also be Auth using same user cookies/token because I don't wanna give another login popup, see lot of MS sample code but no luck
Which approach is right 1 or 2, and share sample code/link any help appreciated,
Per my understanding, you have a client app named Billing App will login users and call APIs via user based access token.
If so , you should register two apps,a client app for Billing App and a service side App .For the client app , as this is a client side, you should use users' credentials to obtain taken from Azure AD instead of app Client secrets. To implement this , you should config it as a public client on Azure AD and enable access token and ID token :
As you want to get call APIs of service side App, you should grant permissions below to make sure that users can get access token via your client app(lets assume "stanapitest" here is the service side App) :
Note, pls click Grant admin consent for Devchat button to finish the permission grant flow.
With this process done , you can get user based access token by password flow :
or OAuth2 grant code flow or Oauth2 implicit grant flow , based on your requirement of course.
Ok, its time for service side, to demo the .net core Api , let's create a simple .net core Api project in VS :
After the project created , change the client id as your service app id :
Run the project and call an API of it , as you can see it has been protected by Azure AD :
Use the user based access token to call this API :
As you can see it works as excepted . Hope it helps . If you have any further concerns , pls feel free to let me know .
After authenticating in Billing app , when you want to acquire access token to access your another web api application , you can call using the acquireTokenSilent method(msal.js) which makes a silent request(without prompting the user with UI) to Azure AD to obtain an access token. The Azure AD service then returns an access token containing the user consented scopes to allow your app to securely call the API.
It's better to make your web api as independent resource which protected by AAD , In your front-end react app , you can use acquireTokenSilent to acquire token for accessing web api after user login :
// if the user is already logged in you can acquire a token
if (msalInstance.getAccount()) {
var tokenRequest = {
scopes: ["user.read", "mail.send"]
};
msalInstance.acquireTokenSilent(tokenRequest)
.then(response => {
// get access token from response
// response.accessToken
})
.catch(err => {
// could also check if err instance of InteractionRequiredAuthError if you can import the class.
if (err.name === "InteractionRequiredAuthError") {
return msalInstance.acquireTokenPopup(tokenRequest)
.then(response => {
// get access token from response
// response.accessToken
})
.catch(err => {
// handle error
});
}
});
} else {
// user is not logged in, you will need to log them in to acquire a token
}
Edit:
Here is my question reformulated:
I have a web server with secured api endpoints - one must have been authenticated with Google prior to using them. I implemented Challenge and Callback endpoints for that.
This works well from a browser with my SPA web front-end. The user gets redirected to the Google website to sign-in and then gets redirected back to my webapp; the browser then has the authenticated cookies and the webapp can use the endpoints to update its state.
I also have a WPF application that will communicate with the web server.
I want the WPF application to do the same as the web front-end: Use the web api endpoints after being authenticated with Google. The connection between the WPF application and my web server is done through an HttpClient.
My problem is I don't know how to authenticate that HttpClient connection between the WPF app and the web server.
I tried using the same Challenge endpoint but the response I get is of course the HTML from the Google Sign-In page, so I guess I can't use that with an HttpClient...
I also tried authenticating with GoogleApis from the WPF app and use the authenticated token to set cookies in the HttpClient but apparently this is not compatible.
How to authenticate an HttpClient connection to a web api with an external provider such as Google?
Original question:
From a WPF application, the user authenticates with Google with this code:
using Google.Apis.Auth.OAuth2;
...
public void Authenticate()
{
UserCredential credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets
{
ClientId = "myClientId",
ClientSecret = "myClientSecret"
},
new[] { "email", "openid" },
"user",
CancellationToken.None).Result;
}
This works and the UserCredential object contains the authenticated token:
How to embed this token information in a web request made with an HttpClient in order to call my webapi endpoint?
I think the request must include some cookies to inform the server that it has been authenticated, but I don't know which ones exactly.
The endpoint on the server-side validates that the user is authenticated with the help of IdentityServer:
var result = await HttpContext.AuthenticateAsync(IdentityServer4.IdentityServerConstants.ExternalCookieAuthenticationScheme);
if (result?.Succeeded != true)
{
throw new Exception("External authentication error");
}
If I got your question right, you just have to set the Authorization header
var credentials = await GoogleWebAuthorizationBroker.AuthorizeAsync(
clientSecrets,
new[] { "email", "openid" },
"user",
CancellationToken.None);
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
credentials.Token.TokenType,
credentials.Token.IdToken);
Maybe you'll find below a helpful hint to better understand OpenID :)
The confusion stems from mixing GoogleApis and IdentityServer frameworks.
Authentication/authorization can be achieved using either of them.
Objects from Google.Apis.Auth.OAuth2 and IdentityServer4 namespaces are not designed to interact.
No manual cookie handling is necessary, for sure.
Ask yourself to whom does Google provide trust for the user. If it calls back to WPF, then webapi trusting WPF is a separate issue.
You answer your own question in the question:
the browser then has the authenticated cookies and the webapp can use
the endpoints to update its state
HttpClient needs to send those same cookies.
How do I set a cookie on HttpClient's HttpRequestMessage
If I understood your question right, then I faced the same problem not too long ago.
The way I implemented it is that in the backend, no matter who tries to access the endpoint, they had to send a Bearer X authorization token.
The token contained the identity of the client that wanted to access the resource, and I checked if he was permitted.
No matter what kind of client wants to access the endpoint, it just has to have that authroziation header in the request that he sends and the backend will treat it the same.
In my scenario, I used an authentication service that returns a cookie to the client with a certain JWT that contains the identity information.
Then from the client I send in every request the JWT received from the authentication service as an authorization header to the backend.
The reason I had to put the JWT that I receive from the service in a header, is that the authentication service and the backend service are not in the same domain, so cookies cant be shared.
This results in such design that no matter how you authenticate the client, the end result must be some sort of token that the backend can receive and read.
Hope this helps.
I have an on-premise Dynamics CRM (2016) that is configured with ADFS (3.0). When a user want's to Login, they get redirected to the ADFS login page and the user enter their Windows AD credentials.
From a .net core application I need to make request to the CRM api using HttpClient. When I try to send the credentials like I normally would for a Windows Auth CRM it doesnt work. I get a 401 Unauthorized. Like below.
HttpClient client = new HttpClient(new HttpClientHandler() { Credentials = new NetworkCredential("myuser", "mypassword", "mydomain") });
var result = client.GetAsync("https://mycrmaddress/api/data/v8.0/accounts");
I also tried using Adal to retrieve a token and attach it as a bearer token to the request but I'm unable to get a token with adal. When I try I receive the following:
The authorization server does not support the requested 'grant_type'. The authorization server only supports 'authorization_code'
ADFS 3.0 doesn't support this flow.
I cannot upgrade to ADFS 4.0 so I would like to know what are my options to make an authenticated call to CRM api (without prompting a login window as this application is a service).
Is there any configuration I can do on ADFS so my first example work? Or is it possible to do it with Adal even if it's ADFS 3.0? Or any other solution...
I found the answer to my question. It's kinda hackish, but I tested it myself and it works. As a temporary solution this will do the trick.
Details are available here: https://community.dynamics.com/crm/f/117/t/255985
ADFS 3.0 supports the Authorization Code flow and this what we will use in this case.
We need to retrieve an authorization code. Normally at this steps a windows is prompted to the user to enter its credentials. By doing a POST and sending the user/password it's possible to retrieve an authorization code.
{authProvider} - ADFS Uri - something like
https://adfs.mycompany.com/adfs/oauth2/
{ClientId} - The Guid used to
by your infrastructure team to add your application to ADFS
{RedirectUri} - The IFD Uri for dynamics - should match the redirect
Url used to by your infrastructure team to add your application to
ADFS
username - The User set up on ADFS and in Dynamics
password - The password for the above user
Then we make the following call with these information using HttpClient.
var uri = $"{authProvider}authorize?response_type=code&client_id={clientId}&resource={redirectUri}&redirect_uri={redirectUri}";
var content = new FormUrlEncodedContent(new[] {
new KeyValuePair<string,string>("username",username),
new KeyValuePair<string,string>("password",password),
});
var responseResult = _httpManager.PostAsync(uri, content).Result;
The response content will be an html page (Remember normally this flow prompts a login page to the user). In this page there will be a form that contains the authorization code. using a library like HtmlAgilityPack retrieve the token. This is the hackish part of the solution.
Now that we have an authorization code we need to retrieve an access token.
For that we need to make the following call
var uri = $"{authProvider}token";
var content = new FormUrlEncodedContent(new[] {
new KeyValuePair<string,string>("grant_type","authorization_code"),
new KeyValuePair<string,string>("client_id",clientId),
new KeyValuePair<string,string>("redirect_uri",redirectUri),
new KeyValuePair<string,string>("code",code)
});
var response = await _httpManager.PostAsync(uri, content);
The response content will be a json string that will contain the access token.
With the access token, make the call to CRM rest API.
You will need to attach the token to the HttpClient in the header as a bearer token.
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer",token);
httpClient.DefaultRequestHeaders.Add("OData-MaxVersion", "4.0");
httpClient.DefaultRequestHeaders.Add("OData-Version", "4.0");
From now on you can make calls to CRM api and you will be authorized. However be carefull normally access token are short lived. You will either need to increase their lifetime or request a new token everytime it's expired.
I'm trying to figure out how to configure/use the Asp.Net Core Authorization with the credentials and user data in another server, accessed through an API.
For example, in the "Auth" server, we have the Auth database, that stores users, roles and claims. Some examples:
GET https://auth.myserver.com/v1/users/4/IsInRole/Admin
The response is a simple bool.
Or:
GET https://auth.myserver.com/v1/users/4/GetRolesForUser/
The response is a list of roles.
The another server is the resource server (also entirely API based, no front-end). It stores a lot of data that demands authorization by roles and claims.
Also, we have a third player: the client.
The client will:
Ask the auth server to get a bearer and a refresh token, using an username and a password
The client stores those tokens and send the bearer to the resource server to get some data
The resource server will ask the auth server if the bearer token is valid
If it is, then the resource server needs to ask the auth server again about the user that has that bearer token, and if that user has the necessary holes/claims that are necessary to access the requested resource.
The 4 step is my doubt. I imagine that should happen on the resource server controllers:
[HttpGet]
[Authorize(Roles = "Admin")] //API CALL HERE TO THE AUTH SERVER (?)
public async Task<IActionResult> GetData()
{
...
Basically, I don't know how to Authenticate an user on the context (just until the request is returned), putting it in the HttpContext.User, using my own custom validation process.
I've tried to simplify this architecture as much as possible to make my question more clear. Please forget about the security on the "Auth" server.
If you are using jwts this flow can be simplified roles can be stored in claims which means that no api call is needed in the authorise attribute.
Also the resource server doesn't need to verify that the token is valid that can be done using a secret.
Look into thinktecture the following are good resources https://leastprivilege.com/ and https://brockallen.com/
I've written a hosted Chrome Web App which authenticates the user with OAuth 2.0 using the Google APIs Client Library for .NET. Now I want to add payments to our application using the in-built Chrome Web Store Payments.
Looking at the documentation it appears that I need an OpenID URL in order to check for payment.
How can I get this UserID/OpenID URL since I'm using OAuth instead of OpenID authentication?
var service = new Google.Apis.Oauth2.v2.Oauth2Service(
new BaseClientService.Initializer
{
HttpClientInitializer = userCredential,
ApplicationName = "My App Name",
}
);
HttpResponseMessage message = await service.HttpClient.GetAsync(
string.Format("https://www.googleapis.com/chromewebstore/v1/licenses/{0}/{1}",
appId,
fedId // Where do I get this??
)
);
I wanted to leave my own experience here so that others can see that it's not just a matter of using the existing Id returned from an authorised request to the user profile endpoint, as this is not the ID required by the Chrome Payments API...
Short Answer
It's not possible to use only OAuth2.0 for a hosted app. The only options for a hosted app are:
Use the deprecated OpenID (see detailed answer)
Use In-app Payments using Google Wallet for Digital Goods
Long Answer
We still have to use OpenID, however Google has provided a migration path for OpenID users to OAuth2.0 called OpenID Connect. The aim of this migration is to map the old fedId field to new the Google+ User Ids.
This allows us to retrieve an OpenID identifier using an existing OAuth 2.0 process.
Caveat: The Google .NET Client APIs do not support this migration path. So authentication must be done manually or using a 3rd party OAuth library.
Howto:
As per usual OAuth flow, direct the user to the Authenticate endpoint (https://accounts.google.com/o/oauth2/auth) with the following variables:
openid.realm=http://localhost ** Required, where http://localhost matches your redirect_uri variable
scope=openid profile https://www.googleapis.com/auth/chromewebstore.readonly ** Both openid and profile scopes are required in order to retrieve the OpenID identifier. The chromewebstore scope is required to query the payments API.
Then exchange the code for an access token from the Token endpoint (https://accounts.google.com/o/oauth2/token)
At this point you will receive the standard access_token, refresh_token, etc variables but also an additional id_token variable.
This id_token is a JWT-encoded string containing the OpenID information.
Decoding this JWT-encoded (you can use this C# JWT Library) string will give you a JSON string in the following format:
{
"aud": "<googleuserid>.apps.googleusercontent.com",
"at_hash": "<hashcode>",
"iss": "accounts.google.com",
"openid_id": "<!! The fedId we require !!>",
"exp": <id>,
"azp": "<googleuserid>.apps.googleusercontent.com",
"iat": <id>,
"sub": "<googleuserid>"
}
At this stage we've finally found what we're looking for, the openid_id. This can be used to communicate with the Chrome Payments API
While still using the same OAuth credentials, make a signed request to the following URL:
https://www.googleapis.com/chromewebstore/v1/licenses/{appId}/{openId}
{appId} is the ID of your app within the Chrome Web Store
{openId} is the openid_id from the JWT response
This should give you what you need:
https://developers.google.com/accounts/docs/OAuth2
Its a complete overview of OAuth2.0.
Helped me with a problem I was having with a webapp setup, hope it can do the same.
P.S - Im not sure but this may be exactly what your looking for:
https://developers.google.com/accounts/docs/OAuth2InstalledApp