Can I grant limited mailbox access to an app-only application? - azure

In brief: can I give my Azure application access to a limited set of mailboxes while still using the Client Credentials Flow (no signed in user)?
More detail
In Azure I have an app-only type application; by this I mean I will authenticate to it using the Client Credentials Flow which means there is no signed-in user present.
Once authenticated I will acquire a token to access Microsoft Graph. I want to use Graph to access a limited set of Office365 mailboxes.
As far as I understand, I can use the admin-only Mail.Read permission scope for this, but this gives my application access to every user mailbox. I would prefer to avoid such broad access.
I think if I used delegated permissions instead, I could use the Mail.Read.Shared scope which would allow my application to read mailboxes that are shared with the signed in user, but then I can't use the Client Credentials Flow because I need to sign in a user, which is what I'm trying to avoid. This is an application which doesn't logically run in the context of a user. (The mailboxes I want to read are all shared, they aren't user mailboxes)
I'm a total Azure noob so I hope this makes sense.
Is this question in the right place?
I'm not sure. On the one hand, there are about 100 azure question in softwareengineering, but there are over 54k on SO, and that's not including sub tags.
On the other hand, I haven't included anything about programming in this question, although it is certainly a programming issue for me, insofar as I have to write the code to authenticate with Azure, get the token, and then access the Graph API, and the answer to this question will probably influence how I go about that. FWIW, here's how I'm currently authenticating and getting a token in C#:
var authority = $"https://login.microsoftonline.com/{tenant}/v2.0";
var authContext = new AuthenticationContext(authority);
var clientCredential = new ClientCredential(clientId, secret);
var token = authContext.AcquireTokenAsync("https://graph.microsoft.com/", clientCredential).Result.AccessToken;
Although I do plan to change that to use a certificate rather than a secret.
If there is a better place for my question (whether stackexchange or other), please do let me know.

App permissions are always organization-wide. The good thing for them is that using Client Creds is relatively simple and reliable. Revoking them would require deleting the role assignment or deleting the service principal entirely. But the bad side is that you cannot limit what it can access.
With delegated permissions, you could make a small web front-end, which allows users to login and give permissions to read their mail.
Your app can then store the user's refresh token securely and use that to access their mail whenever.
The good side is that this limits access to only those users who explicitly give permissions.
The bad side is this requires more work and is not as reliable, since refresh tokens get revoked if a user's password changes (for example).
And you must make sure you store the new refresh token you get when acquiring a token with the older refresh token.
TL;DR: There is no "right" answer to this. Both approaches have pros and cons, and neither is perfect. If your app cannot handle losing access for a while, then you must use app permissions.

Related

Delegated permissions can be used, even if I am not using the app

When consenting as a user to delegated permissions an app requests in the Azure portal, I always find a note in the top, saying "Maintain access to data you have given it access to", like:
What I do not understand is the phrase:
"Allows the app to see and update the data you gave it access to, even when you are not currently using the app"
How do I understand this? I thought that after I consent to this, the app can get an access token for the requested data, but only for the lifetime of this access token.
When I never use the app again, the app wont have another valid access token, therefore cannot access my data, right?
But why should the app use my data, when I am not using it?
For application permissions I would understand the phrase... but in this case it does not make a lot sense.
It means the app also gets a refresh token that it can use to get new access tokens when needed.
They are a feature of OAuth.
Refresh tokens can be revoked for various reasons, as this documentation shows: https://learn.microsoft.com/en-us/azure/active-directory/develop/access-tokens#token-revocation.
If they did not exist, the app would have to redirect you to authenticate again every hour.
We have used refresh tokens to build background synchronization processes for example.

Why do access tokens issued by AAD contain information about the user?

I read a lot about OAuth 2.0 and OpenId Connect and in theory I understand both concepts now.
But if I go into practice, some things are still confusing for me and I hope you can enlighten me in some way...
First thing is, that in all code samples how to secure a .net core API in an AAD-environment I find lines like this in the configure-section:
app.UseAuthentication()
and lines like this in the ConfigureServices section:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = "https://login.microsoftonline.com/xxxxxxxx";
options.Audience = "xxxx-xxx-xxx-xxx-xxxx";
});
However, to access my API I am not using an ID token, but an access token what is Authorization, not "Authentication" like in the code samples.
This works - but I do not understand it 100%.
So my first question is:
Is the access token also "authenticating" in some way?
The second thing:
I read that access tokens have no standardized format. They can be JWT or not, can have an audience or not etc. For this reason you could even put user information in the token like microsoft does. The access tokens contain claims like a "family name" or "given name" etc.
Id tokens in contrast have a standardized format to ensure that authentication is done in the same way by everyone.
If people are accessing my apis with an access token, I can read their name or e-mail address with "user.identity.name" for example. This value I can use to store the information who edited something or who inserted something.
So I am fetching information about the user with access tokens!
So my second question is:
Am I doing the right thing here? Or should this be done in another way.
and:
Should access tokens ever contain information about the user?
Is the access token also "authenticating" in some way?
Yes.
Your API is validating the access token when it receives it.
This is the authentication part, when your API verifies that the token signature is valid, came from the Azure AD tenant that you trust, that it is meant for your API and that it has not expired.
The authorization is when you check what permissions the token contains.
Your API can define different permissions that are given to client applications, allowing them different levels of access.
A valid token can pass authentication, but it might not pass authorization if it lacks the necessary permissions.
Am I doing the right thing here? Or should this be done in another way.
Fundamentally your are doing the correct thing.
The token tells you who the user is that is using the client application, and if you need to know who it was who did something, that's the info you use.
However, if you really want to connect an action to a user, I suggest you use their object identifier / object id / oid instead of their name / username as those can change.
Unless you just want their display name.
Should access tokens ever contain information about the user?
In the context of Azure AD, an access token will always contain info about the user if a client application is accessing an API on behalf of a user.
This includes authentication flows like authorization code, device code, implicit, and on-behalf-of.
They all use delegated permissions aka scopes to call APIs on behalf of the user.
Thus the token contains info about the calling app and the user.
If an app acquires an access token using the client credentials flow where a user is not involved, there will be no user info in the token.
In this case, application permissions are used instead of delegated permissions in Azure AD.
An application acts as itself, not on behalf of any user.
If your API supports both of these scenarios, sometimes the tokens contain user info and sometimes not.
The part about token formats is basically correct from a specification standpoint.
OAuth doesn't define a strict format for access tokens, while OpenID Connect does define one for ID tokens.
Using an access token to call your API is definitely correct.
The ID token is only meant for the app that initiated the user authentication, not for any APIs that it calls.
That's what access tokens are for.
if you check here: https://learn.microsoft.com/en-us/azure/active-directory/develop/access-tokens
in the newer access tokens, by default it follows the standard of not having any personal information about the user. which is what the standards suggest, Access tokens should not contain much or any information About the user. rather just information for authorization (things that user can access).
However you can always add the optional claims (and azure lets you do it) for personal info, but best practice suggests you shouldn't.
in terms of the addauthentication: Authentication is basically proving who you say you are. addauthentication(), basically calls microsoft azure ad to perform this task, saying hey aad please ask this person who he is, azure then checks and says ya this is a real person, but i won't tell you anything about him other than an id, and they have access to your api/application. So from your snippit, it's fine.
at this point, your serverside shouldn't have any personal information about the user, just that they have access and what scopes/roles. If it wants info about the user, it should then take that authorization (access token) and request it from whatever endpoint that token has access to (graph)
here's a great read about it: https://auth0.com/blog/why-should-use-accesstokens-to-secure-an-api/
Hopefully this helped clarify somewhat, and not add more confusion to the issue.

Query users from different azure tenant

We have a case where we have 'clients'. Every client is an different Azure tenant but we keep their tenant id in the database. So we have Angular application where we want to have like a dropdown with all the clients and based on the selected client to query their tenant users so we can add him to our database and give them permissions and stuff to all other applications. As per my readings this in not achievable,
Because this permission application will be used from like 3-4 guys which are part of our tenant only.
Is there a way we can achieve that?
You would need to use the User.Read.All Application permissions and authenticate using the Client Credentials grant. You would then need to retrieve a token from each tenant prior to calling /v1.0/users.
Note that this will require receiving Admin Consent from each tenant you need to query.
Rohit's comment below is an excellent point. If your app is a SPA, meaning the authorization is happening entirely in the browser via Javascript, you're really limited to the OAuth's Implicit Grant.
To use Client Credentials or Authorization Code grants, you need some kind of backend API to handle the authentication and calls to authenticated APIs. I would argue that you should be doing this anyway, if for no other reason than forcing your user to reauthenticate every hour isn't a great user experience.
If you don't mind requiring each user in the tenant to authenticate, you could use the Authorization Code grant. This is a bit more complex of a set up because it requires you to keep track of separate Refresh Tokens for each user. Your backend would need to retrieve the Refresh Token, Exchange it for a set of new tokens (access_token and refresh_token), Store the new Refresh Token, and then call the API using the new Access Token.
Since there is a 1:1 relationship between the Token and the User so, at scale, you're looking at a lot of tokens. You'll also need a bunch of maintenance workflows to handle issues that may come up (refreshing the token fails, new scope requirements, etc.).
It really comes down to the depth of the relationship between your app and the tenant. If you're providing security and analysis to the entire organization, then asking for global Mail.Read is certainly reasonable. If you're providing a service to just part of an organization, it can be hard to get IT to sign off on such a broad permission scope.

OAuth authentication client side security issues

I understand the security issues around attempting to use OAuth for authentication from a provider's point of view. However I've been asked to provide users the facility to log on to a new web application using OAuth and obtain their basic identity info from the likes of Google and Twitter, from which a new user account within the client application will be created. Additionally users will be able to regster/login directly via user/passwords for anyone not wishing to use third party accounts.
We do not require any access to the user's details/info or providers APIs, just their basic identity when they first logon, and of course allow them to login via the provider in the future. Not exactly the use case OAuth is intended for, OpenId would have been preferred, but OAuth has been specified and without valid concerns would need to be adhered to.
My question is how safe is it to assume that the user has correctly authenticated themselves with the relevant provider. If I trust say Google to perform adequate authentication and I obtain an access token and their identity, presumably I can consider that a legitimate user? There are obviously issues if some one has access to the resource owners machine and saved passwords in the browser but that issue is present for those users who elect to register directly.
Presumably it possible to fake an access token, e.g. man in the middle pretending to be google? A MITM could fake an access token and supply identity details that matched a registered user's google id? I don't see anything for a client to know that the information definitely came from the provider. Obviously this problem is not unique to OAuth.
Are there another ways someone could illegitimately access an account that used OAuth to authenticate themselves.
OAuth allows that an application to access a specific user resource (that has been provided permission by the user) and it cannot go outside that scope. I have not seen the documentation that refers to creating a new user using OAuth based application.
That being said:
We do not require any access to the user's details/info or providers
APIs, just their basic identity when they first logon
This violates OAuth authorization process. The Service Provider does the authentication and provides the relevant tokens (based on the success of the authentication). This is to ensure that there are no 3rd party authentication done during the OAuth authentication process.
My question is how safe is it to assume that the user has correctly
authenticated themselves with the relevant provider.
This all depends on the service provider itself. To conform to OAuth protocol, one of the requirement is that user authentication must be done in a secured transport layer with a digital certificate (for HTTP, it must be done in HTTPS). OAuth consumer don't have any reference to the authentication process. Also the authentication process basically asks the user if the consumer can access the resource of the specific user (and not anyone else, since he doesn't have authorization to it).
Is it possible to fake an access token, e.g. man in the middle
pretending to be google?
Spoofing a Service Provider IS possible but it'll be tedious. For one, you will have to create a whole OAuth handshake process, create the exact API as the service provider, also setup an environment that is secured (as OAuth recommends). The only thing the spoofing service provider can obtain is the client credentials. If it has its user credentials, there is no need to use the application as there is no way of providing a user credentials using an application to do malicious damage.
Secondly, access tokens do expire so even if you spoof and retrieve an access token, the original application owner can ask for the service provider to block the application and the access token can be useless.
A man in the middle attack won't be possible. You will have to replicate the service provider in a sense that the end user won't be able to distinguish between the original and the spoofing service provider in order to capture all relevant credentials (from both the application and end user).
Sadly saying, the scenario from your last sentence is the truth.
But you should realise that the security is a huge and complex issue, especially in client side. It's not happen just in a single point but many points through the whole internet access life cycle. The scenario you given is not what OAuth try to solve.

How to keep the client credentials confidential, while using OAuth2's Resource Owner Password Credentials grant type

We are building a rest service and we want to use OAauth 2 for authorization. The current draft (v2-16 from May 19th) describes four grant types. They are mechanisms or flows for obtaining authorization (an access token).
Authorization Code
Implicit Grant
Resource Owner Credentials
Client Credentials
It seems we need to support all four of them, since they serve different purposes. The first two (and possibly the last one) can be used from third-party apps that need access to the API. The authorization code is the standard way to authorize a web application that is lucky enough to reside on a secure server, while the implicit grant flow would be the choice for a client application that can’t quite keep its credentials confidential (e.g. mobile/desktop application, JavaScript client, etc.).
We want to use the third mechanism ourselves to provide a better user experience on mobile devices – instead of taking the user to a login dialog in a web browser and so on, the user will simply enter his or her username and password directly in the application and login.
We also want to use the Client Credentials grant type to obtain an access token that can be used to view public data, not associated with any user. In this case this is not so much authorization, but rather something similar to an API key that we use to give access only to applications that have registered with us, giving us an option to revoke access if needed.
So my questions are:
Do you think I have understood the purpose of the different grant types correctly?
How can you keep your client credentials confidential? In both the third and fourth case, we need to have the client id and client secret somewhere on the client, which doesn't sound like a good idea.
Even if you use the implicit grant type and you don’t expose your client secret, what stops another application from impersonating your app using the same authorization mechanism and your client id?
To summarize, we want to be able to use the client credentials and resource owner credentials flow from a client application. Both of these flows require you to store the client secret somehow, but the client is a mobile or JavaScript application, so these could easily be stolen.
I'm facing similar issues, and am also relatively new to OAuth. I've implemented "Resource Owner Password Credentials" in our API for our official mobile app to use -- the web flows just seem like they'd be so horrible to use on a mobile platform, and once the user installs an app and trusts that it's our official app, they should feel comfortable typing username/password directly into the app.
The problem is, as you point out, there is no way for my API server to securely verify the client_id of the app. If I include a client_secret in the app code/package, then it's exposed to anyone who installs the app, so requiring a client_secret wouldn't make the process any more secure. So basically, any other app can impersonate my app by copying the client_id.
Just to direct answers at each of your points:
I keep re-reading different drafts of the spec to see if anything's changed, and am focused mostly on the Resource Owner Password Credentials section, but I think you're correct on these. Client Credentials(4) I think could also be used by an in-house or third-party service that might need access to more than just "public" information, like maybe you have analytics or something that need to get information across all users.
I don't think you can keep anything confidential on the client.
Nothing stops someone else from using your client id. This is my issue too. Once your code leaves the server and is either installed as an app or is running as Javascript in a browser, you can't assume anything is secret.
For our website, we had a similar issue to what you describe with the Client Credentials flow. What I ended up doing is moving the authentication to the server side. The user can authenticate using our web app, but the OAuth token to our API is stored on the server side, and associated with the user's web session. All API requests that the Javascript code makes are actually AJAX calls to the web server. So the browser isn't directly authenticated with the API, but instead has an authenticated web session.
It seems like your use-case for Client Credentials is different, in that you're talking about third-party apps, and are only serving public data through this method. I think your concerns are valid (anyone can steal and use anyone else's API key), but if you only require a free registration to get an API key, I don't see why anyone would really want to steal one.
You could monitor/analyze the usage of each API key to try to detect abuse, at which point you could invalidate one API key and give the legitimate user a new one. This might be the best option, but it's in no way secure.
You could also use a Refresh Token-like scheme for this if you wanted to lock it up a bit tighter, although I don't know how much you would really gain. If you expired the Javascript-exposed api tokens once a day and required the third-party to do some sort of server-side refresh using a (secret) refresh token, then stolen api tokens would never be good for more than a day. Might encourage potential token thieves to just register instead. But sort of a pain for everyone else, so not sure if this is worth it.

Resources