We have an application built on a static front-end and an API hosted under a separate subdomain (e.g. web.theapp.com vs api.theapp.com). We've recently implemented a document management component built on top of Azure Blob Storage, with custom metadata in the API. When downloading a file, we want the user to get to the API (for authorization checks etc) but then be given/redirected to a blob storage URL with a short-lived SAS token. We have the API part "done" insofar as we can accept a request, do all the required access checks, create a SAS and generate an URL to the file, and redirect there.
HOWEVER: For authentication in the API currently uses Bearer tokens, which obviously won't work on GET requests that result from clicking on links.
Is there a good "goto" solution for this type of problem?
We've been thinking about creating a page saying "Your file downloads in 3... 2... 1..." like the old-school hosting services, but that feels a little tacky. We've also considered adding some sort of cookie scheme, so the API can accept authentication either with bearer tokens or with cookies, but we've failed to find info on how to configure cookie authentication (relevant tech stack: Azure AD OAuth2 and ASP.NET Web API 2).
Is there a good pattern for this type of problem?
Related
I'm learning authentication/authorization in ASP.NET Core and got confused by which components I should use in my scenario: I have an SPA frontend and an ASP.NET Core API backend. I'm using a third-party OpenID Connect provider (like Okta) that only supports Authorization Code Flow, so I believe only the backend should talk to this external provider. The external provider only handles authentication, not authorization. Our application needs role-based access control.
Do we need Identity Server, considering authentication is already handled by the external OpenID Connect provider?
Do we need ASP.NET Core Identity?
How should we approach authorization in this scenario?
(I assume by "Identity Server" you're referring to IdentityServer4 or maybe even IdentityServer3)
Do we need Identity Server, considering authentication is already handled by the external OpenID Connect provider?
No.
People use IdentityServer4 when they want to self-host their own IdP (and of course, you can still federate with Google, Facebook, Apple, etc when you self-host your own IdP too), but when you use an IdP-as-a-service like Okta or Azure AD then you don't need to run your own IdP.
Do we need ASP.NET Core Identity?
No.
ASP.NET Core Identity is basically a utility-kit and time-saving library that (ostensibly...) makes it easier to crank-out the usual user-management features every traditional web-application needs: things like Registration, Password Resets, Automatic Lockout, Email+Phone verification, are all handled for you by ASP.NET Core Identity - including the user database via Entity Framework.
...but when you use an IdP-as-a-service then those same features (Registration, Forgot-password, etc) are supposed to be part of the service you're paying for (so you could either spend tens of thousands of dollars of dev-time building a self-hosted IdentityServer4 IdP - or you could skip all that and pay Okta merely tens of dollars per month instead. Guess which option your project budget manager wants to go with...
With ASP.NET Core Identity, or any self-hosted IdP where you have your own Users database tables, you'll need to query your own database to get user details (username, display-name, etc) (though normally you would store current-user-details in a structured security-token (JWT, SAML, ClaimsIdentity/ClaimsPrincipal, AuthenticationTicket, etc) when the user authenticates a new session so you avoid hitting your database on every request)...
... but when you use a non-local OIDC IdP (like Okta) those user details are stored and managed by Okta and also held by the client and then forwarded to you via the front-channel, or passed directly via the back-channel; those user details ("claims" in OIDC, known as "assertions" in SAML) are contained in 2 separate tokens: First, the id_token holds non-security-related user profile fields, like display-name and avatar image URI, while the access_token holds security claims and scopes (e.g. user role membership, account-is-locked-out status, etc). If your clients don't send you an id_token then you can always use the IdP's user-info endpoint to get the same data yourself (don't forget to cache it locally, otherwise it's even more expensive than hitting a local DB on every request).
SAML works very similar to OIDC, just with its own terminology for almost identical concepts. As an aside, I'm far more familiar with OIDC and OAuth2 than SAML (with which I have zero experience with), but AFAIK SAML uses a single token for both identity-claims and security-claims assertions whereas OIDC splits it up into id_token and access_token (I might be wrong on this...)
How should we approach authorization in this scenario?
Use declarative Authorization Policies which validate/authenticate based on specific user security claims in the access_token (do not use the id_token for authorization).
But while you can use the same C# Attributes for declarative authorization in your server-side code, be mindful that ASP.NET Web API clients and ASP.NET MVC browser visitors do things differently:
In ASP.NET Web API, clients might be an user-interactive smartphone app or desktop command-line program (think like Azure PowerShell) - or it could be a headless background-worker or daemon process. Most importantly: all these clients share the ability to store secrets like Bearer Tokens locally and securely as they're programs running with some kind of read/write access to their local-disk.
(While JS SPA clients use ASP.NET Web API, they don't have the ability to store secrets, so they're a special case that I discuss later on).
These clients all send their access_token in the HTTP Authorization: Bearer 3q2+7w== request header, and ASP.NET Core's built-in JWT authX features will compare the claims in the access_token with your declared policies and use that to accept or reject the HTTP request.
Whereas ASP.NET MVC clients are all web-browsers (Chrome, Firefox, etc) which use traditional HTTP cookie-based authX + sessions.
Browser clients have their access_token (and optionally their id_token) stored verbatim inside their ASP.NET security cookie - or the ASP.NET security cookie stores some short reference to a persisted token cached server-side to prevent cookie bloat.
This security cookie is symmetrically encrypted by your web-application and marked HTTP-only, so even hostile injected scripts (XSS, etc) in a browser-page cannot access and read those token strings.
Caution: because access_token and id_token blobs can be quite large (easily multiple kilobytes) you'll hit browsers' cookie length limits (4096 bytes?), so many web-applications just store all tokens privately (in Redis or memcached or something) and merely store a reference to the cached tokens.
When it comes to JS SPAs (Angular, etc) things are complicated: the client is ostensibly a user's web-browser (well, it's actually JavaScript code in the browser) and so it will make fetch-based requests in the background (instead of browser foreground "top level" Document requests) using an access_token in the Authorization header instead of using cookies (which wouldn't be accessible to scripts anyway, due to it being HTTP-only and encrypted, assuming the even were in the ASP.NET security cookie in the first place) - but because JavaScript clients simply do not have any way of securely storing secrets locally (window.localStorage is not private), so SPAs had their own separate OIDC flow: the Implicit Flow, but it's basically insecure - that, combined with browsers' disabling cross-origin cookies and other techniques that OIDC's Front Channel relies on basically means that SPAs now should not make their own fetch HTTP requests to external RPs (i.e. the backend ASP.NET Web API service for an SPA front-end) but should instead be served from a simple ASP.NET MVC which proxies requests to external RPs, and so the SPA actually uses Cookies, not Bearer Tokens - and means that the somewhat nice idea of hosting an SPA in static AWS S3 or Azure Blob storage is now dead.
So if you're planning on building an SPA, you really should give that
LeastPrivilege.com article a very good read so you understand how the browser landscape is changing (Dominick Baier is one of IdentityServer's creators and maintainers).
I will say that properly grokking OIDC is hard - I built a self-hosted IdP using IdentityServer4 and while I got something-out-the-door after a few months it still easily took me over a year to truly understand what was actually going on.
I have a .NET Core application consisting of a web front-end written as a single page app in React JS and backed by a .NET Core API services app to supply it's data. They're deployed as separate applications in IIS so the urls are like this.
https://example.com/FooBar
https://example.com/FooBarAPI
I've been tasked with integrating the FooBar site with our corporate SSO which uses SAML and authenticates against ADFS. They've provided me with a metadata xml file from ADFS that as I understand it this metadata contains all the details I need to put in my web.config to get this working. I've found some good examples on stackoverflow for getting the FooBar site protected from unauthorized access and working with SSO.
But what about the API app that the javascript needs to call? How does that piece of the puzzle fit? How does the API know that the JS client is part of the same app/user and that they are authenticated to make requests for data?
How does the API know that the JS client is part of the same app/user and that they are authenticated to make requests for data?
Client registration identifies the JS client. The implicit flow and its resultant id_token identifies the end-user.
These are the very high level steps:
Register the JS client (the public client).
From the JS client, use the ADFS implicit flow to fetch an end-user id_token.
From the JS client, call your API with the id_token.
This documentation appears to match your use case reasonably well and provides a high level overview:
...when the user signs in, the JavaScript front end uses Active Directory Authentication Library for JavaScript (ADAL.JS) and the implicit authorization grant to obtain an ID token (id_token) from Azure AD. The token is cached and the client attaches it to the request as the bearer token when making calls to its Web API back end, which is secured using the OWIN middleware.
If I were in your shoes, I would first make the demo application from that documentation work with my ADFS tenant. Then, I would translate its setup to my React/ASP.NET Core app.
I am trying to wrap my ahead around using JWT to secure a WEB API written in C#, but am getting hung up on a few things. From my understanding the flow should be something like this:
Client provides username/password to the Web API from some client application (Angular, .NET, Mobile, etc)
The Web API validates that the username/password is correct and then generates a JWT (JSON Web Token) that contains the user's roles, information, expiration date, and other relevant information.
The JWT is sent back to the client application.
The client application hangs on to the JWT and sends it with future requests.
Assuming the above is correct (and please let me know if it is not), I am having trouble understanding the following things.
Once the Web API has validated the username/password and created the JWT, how does the JWT get passed back? Do I somehow add it to an HttpResponseMessage object? I can't seem to find a clear answer on this.
How should the client application pass the JWT back? Is this in the JSON data, appended to the URL, added to headers?
I see plenty of tutorials referencing OWIN and OAUTH. What are these and why do I need them? I am holding the user credentials and roles in the database used by the WEB API.
Once the Web API has validated the username/password and created the
JWT, how does the JWT get passed back? Do I somehow add it to an
HttpResponseMessage object?
Common practice is on success, the response from the service has the status code 200 OK in the response header, and token related data in the response body
200 OK
Content-Type: application/json;charset=UTF-8
{
"access_token": "NgCXRK...MzYjw",
"token_type": "Bearer",
"expires_at": 1372700873,
"refresh_token": "NgAagA...Um_SHo"
}
How should the client application pass the JWT back? Is this in the
JSON data, appended to the URL, added to headers?
Using the access token to make authenticated requests
Now that you have a token, you can make authenticated requests to the API. This is done by either setting the HTTP Authorization header or query string in the request depending on how the server is configured.
in a header
Authorization: Bearer NgCXRK...MzYjw
as a parameter
GET http://localhost:35979/v2/endpoint?access_token=NgCXRK...MzYjw
I see plenty of tutorials referencing OWIN and OAUTH. What are these
and why do I need them?
OWIN — Open Web Interface for .NET http://owin.org/
OWIN defines a standard interface between .NET web servers and web
applications. The goal of the OWIN interface is to decouple server and
application, encourage the development of simple modules for .NET web
development, and, by being an open standard, stimulate the open source
ecosystem of .NET web development tools.
OWIN OAuth 2.0 Authorization Server
The OAuth 2.0 framework enables a third-party app to obtain limited
access to an HTTP service. Instead of using the resource owner’s
credentials to access a protected resource, the client obtains an
access token (which is a string denoting a specific scope, lifetime,
and other access attributes). Access tokens are issued to third-party
clients by an authorization server with the approval of the resource
owner.
I currently have two web applications, one of which is a web api, which both have it's own implementation of ASP.NET Identity for authenticating users (first one is cookie and second one is bearer tokens).
I need, in the near future, to add a third web site to the family and was thinking to move the authentication logic in a standalone web application which could serve all the orhers.
For example: instead of authenticating to www.mysite.com and/or api.mysite.com, I would like that both the applications could authenticate against a new website, like e.g. login.mysite.com, as microsoft does.
How can I achieve something like this? Does somebody has any direction to start with?
What you need is an Identity Server which will authenticate the requests and give you back an access token. You can then use the access token to make subsequent requests to APIs and websites etc. As long as the access token is valid you will be able to make requests. There is great .net based open source solution available for Identity Server. Have a look at Thinktecture. You can download the source and demo projects to try yourself.
I looked everywhere for an answer about securing my webApi but always i'm pointed to OAuth or openID, but i don't want the user to login or authenticate. This is my problem:
I have a webapi which is called via Javascript to get the data in Json. This data is used in the Html of that page. (deployed on Azure)
Now i don't want someone else to get that data via Javascript or with a simple GET request. Only my html page is allowed to do so.
I'm looking for something to secure my Webapi to be only consumed by the applications i want. If i look to the OAuth and Azure stuff, people always have to login, but i want this page to be public, but only the webapi needs to be secure.
I can't check on IP, because the call is done at client side in the browser.
It is not possible to authenticate and thus secure the API to be used by a specific client (run entirely in the browser - like SPAs) app. You cannot protect the data the app sends to the API. (see for more details Protecting REST API behind SPA against data thiefs)
You could build some modules server side and then use some token based server to server communication if you do not want to introduce users and end user authentication.
Of course it is also a question of how you interpret security.
If it is just that you do not want other web apps to use the data -
CORS policies will do the job. It is unlikely that there will be more
than some isolated cases of users using a browser other than the
popular once which respect CORS.
If it is you do not want data to be mass downloaded you could
implement some client filtering based on IP. This could even be done
on the networking layer so the API do not need to be modified in any
way.
As a suggestion, you can have it secured making sure the request has some headers defined by you. For example, you can set an Authorization header with a token that only you know. You can for example, create a token based on a key that you and the webapi share, encrypt it with the time you are calling the api. The web api receives the request and:
1 - Check if the request has the token it requires;
2 - if it does, it creates a token the same way your page did;
3 - matches its token with the requests token;
If you are calling the webapi via javascript, the method may be exposed. However, it's one idea
Take a look to CORS (Cross Origin Resource Sharing), it may be your solution.
In synthesis you can allow requests to the Api only from some specific websites. Most, nearly all browsers, support it.
This is how you enable and configure it in Web Api 2.