How to protect APIs using the Microsoft identity platform

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[MUSIC] Matthijs Hoekstra: Hello. My name is Matthijs Hoekstra. I'm a Program Manager in the Azure Identity Engineering Organization. And in this video, we're going to talk about protecting your APIs. Well, let's talk about authorization. We have talked about authentication where you login users, this is about protecting resources, we call this authorization. So, when you talk about protecting APIs or authorization, there's different places where this actually starts happening. My browser single page app might be calling an API, maybe I've got a native application or a background process who has to be calling my API, but sometimes APIs call APIs as well and there's different ways we can do it and actually help have you flow your user's information through those APIs as well. When you protect an API with Azure Active Directory, first of all, we have to register your API. Remember, everything in Azure Active Directory is an application, so doesn't matter if it's a website, a single page application, a background process for PowerShell or .NET application, or your API, Azure Active Directory or Azure Active Directory B2C, all the same, have to know about your application. So, first of all, you have to register the app. Then we can define what kind of permissions your application, or your API, actually needs to expose. It's a best practice to keep a least privileged approach, you can create one set of permissions, so that's an all or nothing approach. And that's okay, we see it often happening in Azure, but you have the capabilities with the permission and consent framework to make it more granular. Try to be conservative with what kind of information users actually have to consent to. So, don't try to go I want access to everything and all, that might scare away your users, just try to build it up. So, once it's all done and an application starts calling your API, you're the resource. We talked in another video about identity and access tokens. If you're a resource, you care about access tokens, this is actually the only time you're allowed to look inside of access tokens. Since you're the API, you have the registration, you have to contract between Azure Active Directory and yourself, you know what's actually going on inside of these tokens. If it's encrypted, you actually know the keys, so as a resource or an API, you actually are allowed, and you should validate access tokens. We do advise you to use either middleware or an existing library. To do this incorrectly makes your app less secure and it's actually really hard to do it secure as well. You as a developer for your API, you are responsible to apply and enforce permissions. That's not something we do. So, once we send you the access token, you have to make sure that this user calling your API is actually allowed to do the things they want to do. And delegated permissions, make sure they don't exceed what the signed in user is allowed to do. You are the developer who is responsible to build that model, so if I need access to a mailbox, all mailboxes but I only have access to one, Exchange is actually determining if that user has access or not. That's not something Azure Active Directory does for you. We grant you the access token with the permissions inside which we send along, but in the end, the resource is responsible to figure out if what the user tries to do is actually okay. So, let's look at the portal and how you set up an application with APIs. So, again, we are back in my most favorite blade in Azure Portal, which is the Azure Active Directory Portal and we can register applications here. So, let's just do a new one for this demo. Demo API. We talked about single and multi-tenancy in one of our videos, an API can be a multi-tenant application as well. So, our Microsoft Graph APIs are all multi-tenant, that means that anybody with an AAD account can access that API in the context of their own directory. I'm going to create a single tenant API for now because it's only going to be used by my own organization. The next thing I need to set up is those permissions we just talked about. I go to API permissions, I can actually add my own permissions, sorry, expose an API, I can actually add my own permissions. Scopes, permissions are the same thing. First of all, it will pop up this URI. Every scope or permission has to be unique. I think well wait a minute, when I use the Graph API, I can actually just use user.read. That's correct. We call this a global scope. We call ourselves special, so user.read is actually graph.microsoft.net/user.read. We just remove that stuff for readability, but in the end it has to be unique. So, this can be anything, any string, just leave it as your unique app ID but this could be something you come up as long as the string is unique in your tenant. In here, I can actually create my own permissions, for example, files.read. In the consent and permission video, we talked about user and admin consent role permissions, this is the setting where you decide which one it is. Okay, this permission is both for users and admins or only for admins and what is the text you actually want to show to the user. Read users files. Allow the app to read users files. And the user consent window. Read users files, and allow the app to read users files. The state can be enabled or disabled, but let's enable it for now and add a scope. So, at this point, if I want to use this API, my client application can actually ask for this permission and that will end up as a scope in a token. So, I just created my own files.read permissions. Let me go back to the CactusAPI. We talked about this API in the permission and consent framework video, it has a few permissions. Catalog View Published and Catalog View All. That's the ones we use in the quiz, so this is how we create it. And I choose to not use a GUID, I use just microsoftidentity.dev, that's the name of my developer tenant. That's it. That's how you create a permission and from now on, an application can actually request that permission and your API will receive those permissions inside of the access token. So, any API secured with Azure AD will require a valid access token and we have chosen to use a so called JWT, or JSON web token. That's the same format we use for ID tokens. This is also the reason why developers, when they start dealing with ID tokens, and then with access tokens, they're like what a minute, I can read these things and there's a lot of interesting information inside of those access tokens, I want to use that information to start my client. Don't do that. The resource and the IDP have a contract if they start encrypting the token, you might not be able to read it in a client. A client should never touch an access token, it should only pass along to the API. And a JWT consists of a header, a payload, and a signature. Some folks might even have heard of introspection, some other IDPs so what they do, they receive a binary blob, an access token, and they actually call back to the IDP and say well, I just received this access token, can you tell me what it is? And the IDP will actually tell the resource what the access token contains. We at Microsoft decided not to do that. It also has to do with a lot of load, we have billions of authentications every day, every round trip we can save actually saves us a lot of money and resources and also response time, so that's why we have a readable format for access tokens, the resource can actually validate themselves. Again, jwt.ms is a really good resource to actually display information about access tokens and ID tokens and explains what the claims in the tokens are. When you validate access tokens, please use a library for signature verification and the decoding of your JWT. If you do it yourself, you open yourself up for security risks, so please don't do it. And the general guidelines are first of all, make sure the audience claim is your app ID, so also your API is an app ID, that needs to be the same. That means that the access token is actually meant for you. Make sure the issuer is actually the ones you trust. Make sure the token is coming from us. It has to be valid, so you have to prevent people replaying an old token. It has to be immutable. Your object ID and the subject are good options, so these things will not change when users actually can change their names or even their email addresses. People do get married and divorced and things will happen, or companies get acquired. Make sure the tokens are actually signed within the valid algorithm. And this link will actually give you more information about the tokens and what you should do when you start manually validating those. We also have application permissions. We talked about in a previous video, sometimes you have an application process running in the background and it needs to be able to do certain tasks as well. We unfortunately don't have a UI in the portal to actually build application permissions, you have to do that manually in the manifest, which I'll show you in a minute. Application permissions are implemented as application rules. Applications do not authenticate as a user, they authenticate as themselves and we use client credentials to authenticate applications against APIs. Once the application authenticates and gets an access token, the access token is passed through the API, and instead of a scope in the token, we actually have roles in a token, which you have to use to figure out what permissions it asked for and what the application tries to do. So, let me show you how to create application permissions. My CactusAPI actually already has an application permission. How do I know? Well, let me show you how I grant permission or ask for permissions in my client application. I go to API permissions, I can add permissions, this is the best practice to do to set up the application, what kind of permissions it needs. I go to, instead of just Microsoft Graph, I go to my APIs, and here's my CactusAPI. And it has a delegated and application permission. Look at the delegated permissions, it has Catalog.View.All and Catalog.View.Published, those are the ones I just created through the UI as well. But it also has an application permission that's called Catalog.View.All. So, I can actually build a background process which calls the Catalog.View.All and for example exports the information. Going back to my API, again, there's no UI. For the user delegated permissions, I have the UI. But for the one I just talked about, the app permissions, I actually have to add it to the manifest manually. I'm looking for the roles part, I'm looking for a type application. This is the application permission and it's called Catalog.View.All. A gotcha is if you want to create an application permission with the same name as a user delegated permission, you have to make sure that the value and the ID are the same as the user delegated permission. And you can find those if you go scroll down, OAuth2 permissions, there's a Catalog.View.All. You have to make sure that you use this ID for your app permissions ID as well. So, the name and ID have to be the same, and the description. But again, you have to add a user of an application role in a manifest with a description and all that kind of stuff to make sure that this application permission is visible. So, let me show you how you can validate tokens manually. Typically, that's something you should do automatically, but I want you to understand the concepts of what the steps are when we actually are validating those tokens. So, let me switch to Visual Studio and I got an application here, it's a WPF app, a client app, it's calling a service, and what I did in the service, in the global asax.cs file is create a method which is always called when authentication needs to happen for an API request. And this is the one where I actually start doing things manually. I'm going to start this app a few times and since I'm talking all the time, it will time out, so I might have to restart the application a couple of times because of time out messages, so you're aware of that and bear with me for now. My background service is starting, there you go. I sign in and it will immediately call. Let's sign in as Megan. Here the method is called. I can step through it. First of all, we want to get the access token and there's a value here. Let me copy this value. And let me go to jwt.ms and paste this token without the double quotes. And it actually gives you a lot information. The audience, this should be me, the issuer, this is Microsoft we trust this one, the expiration times and dates and all the stuff. My app ID is here. There's an IP address, there's a name, the OID which is unique, I got my scope. This is user_impersonation, that's not the right one. The subject, the tenant ID, etc. So, this example application, the API has just one permission, it's called user_impersonation and that's the scope which is actually available in this token. When I go to the claims tab, we show this issuer, issued at, not before, it explains all the different claims you care about. This is all the information you can use as a developer to figure out if this is what the user tries to do, if that's okay, yes or no. Switch back to Visual Studio. I can continue browsing, it will check if there's a token or whatnot. So, how do I actually determine if the token is valid? So, the interesting thing here is well this is checking if that information we need to validate a token is cached or not for the last 24 hours, and it should not be because this is the first time I called it and here's the first timeout I just talked about. But we start the app. And step over this stuff a bit quicker. So, Azure Active Directory published so called DiscoveryEndpoint. If I copy this URL and I actually have this page open here, this is the information we publish about our endpoint, what kind of modes we support, where the public keys are, the authorization and token endpoints, etc., etc. This is what the middleware, the ASP.NET middleware also uses to figure out how to set up proper AAD protection. The interesting part I'm interested in here is the public keys. So, every token has a signature, that means that Azure Active Directory signs the token and actually tells us what public key I should use to validate that token. If I go back to my decoded token, we tell you look, this is the key ID which I used to sign this token. The private key's kept by us heavily secured, but the public key is something I should download and figure out if the combination is correct. So, in the keys when I go back to DiscoveryEndpoint, there's a URL to where all the keys are if I opened up that one. Here are actually the keys. So, the one I just used should be here as well, so here this is the key and we got a few because we can rotate keys, and that might not happen all the time, but it can happen. And here's the public key I should download, and this is the public key I should use to validate that signature. Let's go back to my code. So, hopefully this is the last time. Since this information doesn't change that much, we download this information, we're going to cache it for 24 hours, and now we're going to create a security token handler and we're going to tell it okay, this are the parameters I actually want you to validate. I want to validate the audiences, I want to validate the issuer, and I want to validate the sign in keys. And this is where I actually validate the tokens, so I still use some library to actually do the validation and if that works out, I got a claims principle with all the information in there as an identity and the identity as claims, so all the information is here. I'm Megan, there's a role claim which is probably empty. So, we just validated the token, we validated the signature by downloading the public key, we validated if it actually was coming from Azure Active Directory. So, why would you ever change the validation behavior? Why wouldn't I just do a default validation anyhow? One of the reasons would be, for example, if you build a multi-tenant application and validation goes for ID tokens as well, I only want to allow people to login if they actually bought a license. So, in a database you keep a list of tenants or tenant IDs so every time somebody logs in, you're going to validate against that list and only allow users in your application if they actually have bought a license. Or for example, you're going to change behavior, you're going to redirect people to a signup page, like hey, welcome to our trial, click here to sign up for 30 days free trial before you sign up for a real service. So, depending on what you need, this would be the place to do it. Another reason would be we've seen customers building a service or an API which is protected by both Azure Active Directory and some other directory, but for example B2C, Azure Active Directory B2C. At that time, you actually have to validate if your token is actually coming from either one of the issuers, so you cannot do it automatically, you have to check against two issuers instead of just one. So, that could be a reason to build an exception as well. So, typically, as a developer you don't have to override these settings, but there are reasons why you might consider doing that. The reason I showed you is just to make you understand that what are all the steps involved. So the API is actually talking directly to the IDP, Azure Active Directory, downloading the public keys so it can validate the tokens which are coming from your applications. We did a CactusAPI example in the permission and consent video. I want to refer back to that API and explain more about what's happening here. So, if I would ask you what does this access token allow you to do, I would be curious what the answer is. Okay, I know, stupid joke, nobody can read and encoded access tokens. So, let's decode the access token and show you this. So, in the CactusAPI call, so I'm the audience, that's correct. There's an issuer here, there's an expiration date, there's a name. The ones we were interested in are the rules, I'm an administrator, and the scope, Catalog.View.All and Catalog.View.Published. This access token actually proves that the application was granted consent to use both of these scopes. So, you as a developer have to interpret this token and figure out what the effective permissions are. So, what, for example, would this token allow you to do? Catalog.View.All is a role, this means it's actually an application permission. We can also see that because the OID and the subject are the same. That's an extra check you can do as a developer, right now you know it's an application permission, you have to look at the rules claim to figure out what the application permission is. I do have a little code sample to show you what it is. So, if you really take a step back and try to understand what you are needing to do as a developer, it's actually not that complicated. You get a token with a bunch of strings, we call it claims, in the end you use all those claims and those strings to get enough context to figure out what your application is supposed to do. Let me see. I got the catalog here. So, I got an API here which is protected and it's the Get Items API. First of all, what I do is get my claims, get my scopes from the token. This is the name of the claim. It's a space separated list so we're going to do a split, so we've got an array of scopes. Then I'm going to find if there's scopes like catalog view all or catalog view published is actually part of that array, and if so, I set this little boolean. I'm going to check if there's an administrator calling this API. I need to figure out if the OID and the subject are the same, so I know it's an app permission, and if that's an app permission, I'm going to find the scope, or the user role in this case, and if it's catalog view all I know there's an app permission with catalog view all available as well. This is just all plumbing to figure out what's in that token, so like string handling, it's nothing more than that. Now, as a developer, I actually have to calculate what the effective permission is. In that catalog API example, an admin was allowed to see all items, and in catalog view all was a user delegated permission where an application was allowed to view all items as well. And the app permission catalog view all items, that should get you access to all the items as well. So, I check all those requirements. So if I'm an admin and the application has the permission catalog view all or I use an app permission, I'm going to return all items. If that's not the case, it's catalog view published or it's catalog view all scopes but I'm not an admin, I'm only going to return all published items. This is what you as a developer have to do to code effective permissions. So, it's nothing more than getting a bunch of strings, and by using those strings figure out what the context is and either grant or don't grant access to certain things. Nothing will stop you by just ignoring all this stuff, just look up the OID because that's this user, you look up in your database okay, is this user allowed to do certain things, yes or no? And if that result doesn't come back so okay, I guess this user's not allowed to call the API, you're going to throw back an unauthorized. Or you know what, by just doing this database lookup, I'm going to figure out what the user is allowed to do and then you can discuss about okay, well how do I cache this information? I don't want to do a database lookup for every API call, etc., etc. That's all an exercise up to the developer, but those things you have to do. What I've seen typically is that the developers know pretty well how to build authorization, that's not new, the only change now is that all that information comes back as claim inside of the principal name. But if you have used all our schemes before, it's the same old, same old. So, since we are now in the token business and figuring out what kind of strings and claims we have in tokens, let's talk about more tokens. So, let's talk about an enterprise application, single tenant, for your LOB application. The interesting part here is that we have the issuer, which is login.microsoftonline.com and instead of /common, it's going to be /tenantID. And the tenant ID is also a claim in the token, and they should be the same. A multi-tenant application will have a different issuer, so somebody else from a different tenant logs into your application, it will be a different tenant ID. So, if you go back to this one and this one, you can actually see there's different tenant IDs. The audience though is still the same. So, if you build a multi-tenant application or an API, you have to make sure that you use the tenant ID to figure out what people are actually allowed to see. So, your responsibility is to make sure that somebody from one tenant will never see information in your application from the other tenant. So, you have to check the audience is correct, you have to write a little bit of logic to make sure that the first part of the issuer is actually us and the second part, the tenant ID, is something you use to uniquely identify the organization the user's coming from. But what happens if I'm actually a guest in another tenant? So, again, the issuer is there, the tenant ID is there, but there will be an extra claim, it's called IDP and it actually tells you from what tenant the user is coming from. Remember when we talked about in the first one of the videos, where you can have the B2B guest scenario, where you can invite people inside of your tenant, so we do create an object for that user, but we know that the user's coming from an IDP. This is what you actually have to use to determine if a user is a guest, yes or no. I don't know if people have experience with Teams, you can actually see Teams showing something like guest behind a name. This is what they have to use to figure out if somebody's a guest in a tenant, yes or no. Again, so the tenant ID and the issuer have the same unique ID but the IDP claim extra adds the information about where the user's coming from and can actually store their tenant ID as well because it's part of that claim. If we invite a user from Gmail as a guest, which we can do too, again, the issuer is there, the tenant ID is there, the IDP will say gmail.com and we actually have a unique name coming from Google you could use as well if you want to. So, again, you're just handling a bunch of strings in a token, and we just call it fancy claims. If you use B2C or Azure Active Directory B2C, you will see that the issuer is different. There's an IDP Facebook because in this case in my account I was using Facebook to login to my application. It will give you an email address and a tenant ID as well. This is just same format as a token with claims and you can use to figure out well, it's a Facebook user, should I allow this, yes or no, if you want to. You just handle the strings, you handle the OID to uniquely identify the user or the subject depending on your scenario and you go from there. So, at the start of this presentation, I also talked about well, APIs can call APIs as well. I don't know if people have used Kerberos and Kerberos delegation in the past, that was actually a pain to setup correctly and make it really secure. There was a lot of permissions needed to set it up. We have a better scenario with modern protocols for that now and we call it On-Behalf-Of flow. It's a standard OAuth protocol feature. So, remember the scenario where an application can call an API and this API actually has to call another API. And instead of just knowing that this API is calling me, I actually want to do that in the context of that user. So, how does it work? So, in one of the videos we explained how you get an ID and access token, assume that my application logs in the user, asks for permissions to call an API, I get an ID token and an access token, I pass along the access token to the API, it will authorize if this is all good, I call the API and I get my results. Now this API wants to call another protected API, so what happens here? Well, the API is going to talk to Azure Active Directory and going to ask well, I need an access token to read the user's profile using the Microsoft Graph on behalf of the user. We have to send an access token and a secret or a cert, which is only known by the API, to Azure Active Directory. And we use that secret or cert credential to get our client ID to login as the API for my web application to Azure Active Directory. We pass along the original access token, and if everything checks out, we get a new access token. This access token is passed along to the API, the API actually checks the access token, it has no clue this one is coming from another API or not from the user. It still has all the information it needs, so for this API, it just looks like the user is calling this API with the on behalf permissions. And if the token works out, you can grant access and we will return you the result. So, how you write this code. We have to get in the Confidential Client Application Builder with your client ID. You pass the secret. Well. don't do it, use the certificate because it's more secur. And this is actually the code you have to do. We use the name, you're going to read the certificate, depending on what device you're running on, it's stored for example in Windows, it's stored in the credential store. And after you get the token, you have read the certificate or used the secret, you call the line application.AcquireTokenOnBehalfOf with the scopes you need to call, the access token. This will result in an access token and you can use that to do the next call. That's it. So, we actually have a really nice model where you can call an API, that API can call another API, you as a client have no clue that all this is happening and that can cascade along nicely. So, this concludes the video talking about authorization, protect your APIs, creating user delegated permissions, creating app permissions, and even calling an API on behalf of a user so you can actually chain along a few API calls. I hope you liked this video and see you next time. [MUSIC]
Info
Channel: Microsoft Azure
Views: 17,317
Rating: undefined out of 5
Keywords: Microsoft identity platform, APIs, Azure portal, Identity for Developers, access tokens, Azure Active Directory, Azure Active Directory B2C, Azure Active Directory portal, Azure AD, Microsoft Graph APIs, AAD account, API permissions, ID tokens, IDP, JWT, CactusAPI, identity, authentication, Microsoft, Visual Studio, Matthijs Hoekstra, Azure Identity Engineering, how to protect APIs
Id: IIQ7QW4bYqA
Channel Id: undefined
Length: 33min 17sec (1997 seconds)
Published: Fri May 15 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.