I've set up two Web Apps in Azure and both are backed by Azure AD Authentication.
One is the frontend and the second is the API backend. Opening every page on their own works well (Login screen), but calls made by the frontend to the backend results in being blocked by CORS policy. Message is:
XMLHttpRequest cannot load hxxps://backend.azurewebsites.com/api. Redirect from hxxps://backend.azurewebsites.com/api to hxxps://login.windows.net/94... has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'hxxps://frontend.azurewebsites.net' is therefore not allowed access.
The call is made as follows:
$.ajax({
type: type,
dataType: 'json',
url: server + '/' + url,
crossDomain: true,
xhrFields: {
withCredentials: true
},
data: data
}
});
The API should not be sending back redirects when a call is unauthenticated. The caller can't do anything useful with that.
Instead you should setup Azure AD Bearer token authentication on the API, with Audience set to the APIs client id, and the Tenant set to your Azure AD tenant id/domain name.
Then your front-end should get an access token for the API somehow, and attach it to all calls going to it. There are multiple ways to do this.
Here you can find a sample Single-Page application: https://github.com/Azure-Samples/active-directory-angularjs-singlepageapp.
More sample apps here: https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-code-samples
Related
I'd like to use MS Graph REST API, I use an ajax get request to sign in the user (https://developer.microsoft.com/en-us/graph/docs/concepts/rest). How should I do that? I get the response just don't know how it'll popup.
This is the sample code:
$.ajax({
method: "GET",
url: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=<app ID>&redirect_uri=http%3A%2F%2Flocalhost/myapp%2F&response_type=code&state=1234&scope=mail.read",
async: true,
cache: false,
dataType: "json",
complete: function(response){
console.log(response.responseText);
},
});
I think you have to redirect the user to that signin page, not just sending a GET request to it. The user is shown a consent screen where they can review the permissions your app is requesting.
Using an auth library is significantly easier than manually handling the redirect to login.microsoftonline.com. There's a guide for authenticating to Graph with MSAL (Microsoft Authentication Library) at https://learn.microsoft.com/en-us/azure/active-directory/develop/guidedsetups/active-directory-javascriptspa
MSAL will help parse the response after the user has consented and is redirected back to your app
Context:
The company I'm working for would like to achieve Single-Sign-On capabilities with a third party that uses the authorization code grant. Our product right now is built in .NET Framework 4.0 using Forms Authentication in a ASP.NET Web Forms application.
Question:
Is there a way to achieve single sign-on with out the user having to sign in twice (once through forms auth, and another through the identity server)?
To elaborate, is there a way to have the identity server authenticate the user automatically after the user has successfully signed in through forms authentication?
Here's the IdSvr3 webforms client: https://github.com/IdentityServer/IdentityServer3.Samples/tree/master/source/Clients/WebFormsClient
From what I've read, IdentityServer3 clients (which are previous to .NET core) are supposed to work with IdentityServer4 (which has to run on .NET core) and will talk to IdentityServer4 successfully.
So I managed to work around my issue by sending the users credentials on login to the identity server via ajax. I made a webapi controller that authenticates the user and sends back 'set-cookie' response headers for the idsrv auth cookie.
So now I'm signed into the original application, as well as the identity server all without the user having to manually sign in twice.
You just have to deal with a bunch of Cross-Origin-Requests stuff. Have to set "AllowCredentials" when setting up the CORS policy for the application to make the ajax request so that the 'set-cookie' headers actually apply.
$.ajax({
method: 'GET',
url: 'https://localhost:44304/api/login?username=testing&password=testing',
crossDomain: true,
xhrFields: {
withCredentials: true
}
})
The api/login endpoint is defined in a Controller that just calls the HttpContext.SignInAsync() extension method.
I'm building a SPA application that uses my API through the OAuth Authentication mechanism. My application doesn't has a login view, so I get a token by writing the username and password in the code same, in this way:
$.ajax({
type: "POST",
url: restServer + "/token",
dataType: 'json',
// insecure :
data: {
grant_type : 'password',
scope : '*',
username : 'someuser',
password : 'p4sSw0rd'
}
})
//...
but obviously it is an insecure way to do this, everyone can read the username and the password.
I tried to find about this problem on stackoverflow, etc. but I don't understand how I can solve it.
someone says to get the user and password from the server, but it has the same security problem.
someone mentions https, but in this first step it is not involved.
someone has ideas?
You should not keep usernames and passwords around in your JavaScript code. They are too easy to be stolen.
SPA application are supposed to use the implicit grant in OAuth 2.0. That means on log in you redirect the client to the authorization server to authenticate. The authorization server will redirect the client back to your SPA after authentication and authorization completed. The resulting access token (JWT token) can be used to make calls to your REST API in the backend.
When the access token expires, you communicate with the authorization server to get a new token. User interaction may be required for this.
How to authorize only my app to use my REST API ?
I have this code in Javascript
$.ajax({
type: 'DELETE',
url : 'removeTest',
data: { ... },
beforeSend:function(){
...
},
complete:function(){
...
},
success:function(data, textStatus, jqXHR){
...
}
});
This call will remove a user from the database with REST API in PHP. The problem is that everyone can remove a user, with POSTMAN (Chrome plugin) for exemple. How can I protect my REST API to authorize only my app.
Check the HTTP_REFERER is not enough. What could be better ?
Thanks for your help
You have several possibilities here. In general you could authorize the user, the app or both. This depends on your application requirements.
Authenticate Applications
To authenticate the application you could use a token based system, such as an API-Key. This means any request would be signed using additional request parameters. Take a look at the way amazon does this in their S3 service for example. If your application will be the only one that will access the rest API you could alternatively simply restrict the acces by the IP address.
If there will be multiple users using the service via your client, you may also need to authorize the access to certain functions. For example: Will every user be allowed to delete any resource? If the answer is no, you have to implement
Authenticate and authorize users
A simple way to authenticate users in a RESTful API is using HTTP Basic or Digest Auth. In this setting the user credentials are sent via the Authorization header in a form of username:password as Base64 encoded hash to the server. Please note that you should only do this via an secured connection using HTTPS!
Additionally you could also take a look at more complex/sophisticated practices like OAuth.
I wrote a set of APIs which will consider you authenticated if you logged on the website (and therefore you have your cookies set).
I then wrote a Greasemonkey and a Google Chrome plugin that does different calls to my api/* calls. However, Ajax doesnt send cookie header over cross domain (remember it is plugin that is enabled when you are on facebook).
What are the best strategies to authenticate my user and authorize his api calls?
GM_xmlhttpRequest
indeed does an AJAX-call and sends the cookie header.
You can therefore simply call a website that returns the authentification status based on visitors cookies, return a simple result and use it.
Example
GM_xmlhttpRequest({
method: "GET",
url: "http://yourwebsite/loggedin.php",
onload: function(resp) {
var conti=resp.responseText;
loggedin = parseInt(conti); //let the webpage return 1 or 0