Web Application Proxy and ADFS Re-Authentication on AJAX calls - javascript

I have an MVC application that is authenticated with Federation Services. All the requests are routed through a Web Application Proxy, which redirects to ADFS for authorisation if necessary, and the security tokens passed to our app.
When the session expires the WAP redirects to ADFS and an automatic reauthorisation happens. Which is fine for a normal page load.
But when a page makes an ajax call we get a 307 redirect, which our jquery ajax calls fail to handle. There is a CORS issue as the ADFS redirect is to a different domain.
I'd like to be able to intercept this call and display a message or redirect to the login page. How can I achieve this?
I have the following restrictions:
We must use WAP
I'm not able to change any configuration on WAP. Which means I can't add an Access-Control-Allow-Origin header to the redirect.
As we're using WAP my app doesn't even know of any authorisation failures and I can't send back a 401 to be handled by the client side code, which is what I'd normally do
So I think I need a JQuery/JavaScript solution to this.
Thanks

Related

Should the OAuth2 Redirect URL be to the frontend or backend?

I'm setting up OAuth2 in my app using the Authorization Grant flow. I am also using create-react-app, such that I'm developing on localhost:3000, which proxies to my app server backend on localhost:8080.
Everything mostly works, except for the fact that I cannot get the CSRF token working.
I realized it was because I was having the OAuth2 Redirect URL set to the backend, and as a result it was not sending the private encrypted csrf_state cookie along, because the request was originating from google instead of my app.
I don't think this will be a problem in production, because there won't be a proxy server. Instead, both the backend and frontend will be served from the same mydomain.com
So, should I just not have this work in development? Or should I have the OAuth2 redirect URL set to my frontend (localhost:3000), which then automatically redirects to the backend (localhost:8080), such that it can send the private encrypted CSRF token along?
Or is there a way to have the cookie originate from google, without having the multiple redirects? Or should I just not bother with CSRF, since SameSite has such large support amongst browsers now?
The OAuth2.0 Authorization Code grant includes CSRF protection using the state parameter. Use this instead of relying on cookies.
state
RECOMMENDED. An opaque value used by the client to maintain
state between the request and callback. The authorization
server includes this value when redirecting the user-agent back
to the client. The parameter SHOULD be used for preventing
cross-site request forgery as described in Section 10.12.
Source: https://www.rfc-editor.org/rfc/rfc6749#section-4.1
Ahmad is right - and here is some more context on standard usage for react apps and APIs:
If you're using React then you have an SPA that should redirect directly to Google during logins
So your redirect url should be localhost:3000
Your SPA should be entirely cookieless - and much simpler - which is one of the benefits of SPAs - also you can turn off CSRF checks in the API
Your SPA will then send an access token to your API and the API will need to validate the token rather than cookies
My tutorial and code sample may help you understand the moving parts:
https://authguidance.com/2017/09/24/basicspa-overview/

CORS issue with Azure AD OpenID Connect logout in Angular app

I have a tenant in Azure which hosts a number of apps. I'm using OpenID Connect for authorization. In the login.microsoft.com openid-configuration, an end_session_endpoint URL is supplied. This article explains how to send a sign-out request. This will end the session in Azure, but not on my client (an Angular2 SPA). To do this, I'm using angular-oauth2-oidc and the following code:
this.http.get('https://login.microsoftonline.com/TENANT_ID/oauth2/logout",')
.toPromise()
.then(function () {
this.oauthService.logOut();
}
);
The GET to login.microsoftonline.com will end the session on the server and the logOut() call will end the session locally. When I run this, I get the following error:
XMLHttpRequest cannot load https://login.microsoftonline.com/TENANT_ID/oauth2/logout. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:51518' is therefore not allowed access.
When I check the response from the login.microsoftonline.com GET, Access-Control-Allow-Origin is not one of the response headers. I'm very surprised it wouldn't have that header with * as the value. In any event, how can I address this? I see there is a CORS setting in App Services and I added my localhost (testing locally currently) to CORS for one of the apps in the tenant, but it's still not working.
Temporarily, I've added an API endpoint on my apps API that makes a call in C# to the login.microsoftonline.com URL, but it's annoying to have to do that. I'd like to do it all through javascript.
You need to redirect the browser to the URL. You can't call it over AJAX.
You must also redirect the user to the end_session_endpoint for sign-out.
Azure AD does not allow cross-domain calls on the login endpoints, and you can't change that.
You can redirect the user to:
https://login.microsoftonline.com/ccb87602-82bf-4f35-b7d2-aa‌​aaaaaaaaaa/oauth2/lo‌​gout?post_logout_red‌​irect_uri=https%3a%2‌​f%2fsitename.azurewe‌​bsites.net%2fHome%2f‌​SignedOut
The post_logout_redirect_uri parameter allows you to specify where the user's browser should be redirected after log out. This way the user won't lose context. Do make sure to save up the user's work on your app's side before redirecting so you don't get angry users.

Automatically submitting form to HTTPS site, how to authenticate?

I'm hacking together a script to automate the submission of tickets to our helpdesk system. So far it's a very simple, but working system. I have a page with a form that auto-submits via JavaScript with form value based on the URL requested.
This works great when you access the page from your browser. Assuming you're already authenticated to the ticket system page, the auto-submitted form happily sends its form data and you are directed to the ticket list where you see the newly auto-submitted ticket. Unfortunately of course, the ticket system is an HTTPS secured site, so if you're not logged in you're directed to the login page and the auto-submission fails.
The idea however is to run this auto-submission on a schedule, or kick it off remotely, where the initiator won't necessarily be human and won't be following the form submission to babysit it with delicious authentication cookies.
So, being a newbie in this area, my options seem to be A) dive in and get real messy by listening for the auto-submission response, determine whether the login page is being returned and submit some credentials via JS (not a huge deal as this automation would run solely on a secured server), then resubmit the form... or B) somehow do this the proper way by authenticating beforehand. But that's where my knowledge ends.
I've read through this similar question, but am still coming up short. Is this proper automation only possible if the server in question supports some form of auth token API? Is there not a more direct way to connect and request/submit data to an HTTPS site? I've been glossing over some introductions to cURL, but have not yet dove in.
NB: I don't have direct access to the ticket database, code, nor to the web server processes/accounts running it. I probably can run processes on the same machine, which is why I'm not real concerned with the security of auto-submitting credentials, but that's probably it.
Firstly, whether your ticket system directs you to a login screen if you're not already authenticated has nothing to do with HTTPS - this will be either a username/password <form> that then sets a cookie, or it will be a WWW-Authenticate header. Each of these can be used whether you are using HTTPS or plain HTTP.
Whichever method it uses, if you're planning on doing this in a web browser, chances are you won't be able to because CORS (cross-origin resource sharing) will probably not have been set up to allow it.
If however you're doing this from a script such as Node.js, Python, PHP or anything else that can make arbitrary HTTP(S) requests, you might want to look at a flow like this:
Request the index page of the ticket system
Detect whether it gave you a login screen
If so, fetch any necessary data from the login screen (e.g. a nonce) and make a POST request as if you filled in the username/password yourself
Check that authentication was successful (based on the POST response)
Keep the cookie returned by your POST request and use it to submit the ticket.
For the simpler case where the system uses a WWW-Authenticate header it would be like this:
Request the index page of the ticket system
Detect the WWW-Authenticate header in the HTTP 401 response received
Send an Authorization header with an appropriate value
Check that authentication was successful (based on getting an HTTP 200 instead of a HTTP 401)
Send the same Authorization header again while submitting the ticket.
Using WWW-Authenticate is described at Wikipedia for basic and digest authentication.

How to authenticate instagram without redirect url? no server

I am trying to write a client side authentication for instagram, however I want to do so without a redirect uri, i will not be hosting a sever.
https://instagram.com/oauth/authorize/?client_id=CLIENT-ID&redirect_uri=REDIRECT-URI&response_type=token
the dev api states this is the implicit way, however i cannot find a solution
The short answer is, you cannot do without a redirect uri.
This is how OAuth works. You need at least a uri to receive the access token. The uri could be hosted on localhost using a simple web server though.
Follow the Client-Side (Implicit) Authentication flow.
Use the credentials from the app you registered at the Instagram developer portal.
So change CLIENT-ID and REDIRECT-URI from that url to the credentials of your registered app.
From your app, send the user to that link.
If they accept they will be redirected to the redirect uri (which should be somewhere in your app).
On the page they get redirected, the page will have a hash containing an acces token.
Get that from the url and use it to request data from the instagram api. You will need jsonp for the requests. See an example in action in a similar thread

ADFS 2.0 and jsonp

I have a main web-site that uses passive federation (ADFS 2.0)
This website has javascript that calls out to an MVC Web API site using jsonp.
I am trying to get this WebAPI to participate in Single Sign On (same machine, different port). However the passive redirects break the jsonp. (The STS returns its own script which the browser renders and i never get to redirect to the real url for my response script)
Is passive federation compatible with a jsonp webapi?
If not, how do I use Active Federation without username/password credentials?
i.e. The user will be authenticated via the main website before calling the webapi, so how do I leverage the fact they are already logged in, in the webapi?
The passive federation protocol won't work in this scenario as you are experiencing.
You have two options:
If your web api is being exclusively used by your website you can share the cookie that is generated by WIF when the user authenticates. To do that, if you are using different websites you should configure the <cookieHandler> section on WIF configuration to use the same domain and path and use a FQDN (instead of machine names), so that the browser identify both the website and the API as the same domain.
The second option is to configure the Web API to extract and validate SAML tokens (being generated during authentication). What you would have to do here is to save the token that was used for authentication (turn on the saveBootstrapToken on the <service> element of the WIF configuration), get it by using the claimsIdentity.GetBootrapToken() extension method and attach the token on the JavaScript call as an HTTP header like "Authorization: bearer ...the-token....". On the server side you have to get that and validate the token (programatically). Note that you might hit a quota in IIS because of the header length if the token is too big.

Categories