The document discusses 7 common mistakes, or "deadly sins", that developers make when building applications that integrate with Azure Active Directory (Azure AD). Each section describes a scenario where a fictional developer named Bob implements a suboptimal solution to a requirement, explains the security flaw in Bob's approach, and provides guidance on how to implement it securely. The document is intended to demonstrate common pitfalls to avoid when using Azure AD for authentication and authorization.
2. Speaker Intro
• Joonas Westlin
• Developer @ Zure
• Azure MVP
• Global #1 on Stack Overflow for Azure AD answers
• Blog: https://joonasw.net
• Twitter: @JoonasWestlin
3. Contents
• Seven scenarios will be presented which have some things in common
• They all involve a fictional developer who we will call Bob
• A real requirement is presented
• Bob was not necessary malicious, mostly just pragmatic
• I will demonstrate the solution Bob came up with
• Why it was not a good solution
• How to do it better
• We will go from less serious problems to more serious issues
• This is very subjective of course
4. 1. Using groups instead of roles
Requirement
• Admins will be added to a group in Azure AD
• Only users in that group should have access to a part of the app
• This is a typical pattern that comes from on-prem AD environments
5. 1. Using groups instead of roles
Solution that Bob came up with
• Search “azure ad authorize by group” on Google
• First result is a sample app that shows how to use “group claims”:
https://azure.microsoft.com/en-us/resources/samples/active-directory-
dotnet-webapp-groupclaims/
• Can set “groupMembershipClaims”: “SecurityGroup” in the app’s manifest
and you get the user’s group ids in the Id token
• Check if the user has the required group id in the token
6. 1. Using groups instead of roles
User clicks
login in app
User logs in at
AAD
User sent back
to app with
token
Token contains
group ids
7. 1. Using groups instead of roles
The flaw
• No security problem in this approach actually
• It does work for most users
• The problem is the maximum size of tokens
• You can have max 200 groups in a JSON Web Token
• “Surely there can’t be users with more than that?“
• If a user has more, their groups won’t be in the token
• Instead, you will have to query them from Microsoft Graph API
8. 1. Using groups instead of roles
How to do it better
• Define an Admin role in the app’s manifest
• Assign the Admins group to the Admin role
• If not available due to licenses, you need to assign users individually
• If user (or their group) has the role assigned, it will appear as a claim in the
token
• Disclaimer: this is mainly my opinion, you can use groups, I prefer roles
9. 1. Using groups instead of roles
User clicks
login in app
User logs in at
AAD
User sent back
to app with
token
Token contains
role
11. 2. Wildcard reply URLs
Requirement
• After a user logs in, they should be redirected to the page they tried to
access
• So:
• User goes to www.contoso.com/products
• Redirected to login & logs in
• Redirected back to app, sees product list
• This one is pretty common, it is better UX
12. 2. Wildcard reply URLs
Solution that Bob came up with
• Bob wants Azure AD to redirect the user back to the right page
• But then realizes that he would have to define all of them in the app’s Reply
URLs
• This is not scalable
• But then he realizes he can just specify one reply URL as:
https://www.contoso.com/*
• And that works
13. 2. Wildcard reply URLs
User tries to
access /products
Redirected to AAD
with redirect_uri
/products
User logs in at
AAD
User redirected
back to /products
A global handler
will grab the token
from the redirect
14. 2. Wildcard reply URLs
The flaw
• Not supported in the new app registration experience
• Support is being removed
• Can allow a malicious actor to redirect the user to a page of their choosing
in the app
• Combine this with an open redirect vulnerability and you have a problem
• Users could be sent an email with a crafted link that brings to them a
phishing site that looks identical to the actual app
• Users might then enter confidential information in the fake app
15. 2. Wildcard reply URLs
How to do it better
• Avoid using wildcard reply URLs
• Store a “local redirect URL” in a cookie / local storage / session storage
• Do a redirect to that after authentication
• Remember that the local redirect URL is user input
• This does not prevent open redirect attacks, but gives you a good point to
validate the URL
16. 2. Wildcard reply URLs
User tries to access
/products
/products stored in
cookie
Redirected to AAD
with redirect_uri
/auth
User logs in at AAD
User redirected
back to /auth
Finalize
authentication,
redirect to
/products
17. 3. Resource Owner Password Credentials
Requirement
• We do not want the login experience to look like Microsoft
• We want to customize it more
• The user should be able to just enter their username and password on our
login page, and that should handle the login
18. 3. Resource Owner Password Credentials
Solution that Bob came up with
• Bob finds that there is a way to get tokens from Azure AD using the
“password” grant type
• He tests this with his test account and it works
• He implements the login page so that it forwards the user credentials to
Azure AD and then stores the user login state in a cookie based on the token
received
• Users are now able to login through the app, without seeing the Microsoft
login screen
19. 3. Resource Owner Password Credentials
User enters
username &
password
HTTP request
to AAD
AAD returns
tokens for user
Authentication
cookie created
for user in app
User is
authenticated
20. 3. Resource Owner Password Credentials
The flaw
• Passwords are now passing through the app, requiring users to trust your
app with them
• It trains people to be phished
• Users with MFA / expired password / federated accounts will tell Bob they
cannot sign in
• Are you willing to disable MFA for a custom login page?
• ROPC is meant to be a legacy migration path
21. 3. Resource Owner Password Credentials
• “Do not use ROPC with new apps” -Nat Sakimura (OpenID Foundation Chairman)
• https://youtu.be/qMtYaDmhnHU
• https://www.scottbrady91.com/OAuth/Why-the-Resource-Owner-Password-Credentials-
Grant-Type-is-not-Authentication-nor-Suitable-for-Modern-Applications
22. 3. Resource Owner Password Credentials
How to do it better
• DO NOT USE ROPC
• Use regular interactive authentication flows
• Azure AD B2C or libraries like IdentityServer can be used to build more
custom login experiences
• Cost of developing an identity provider is big, be careful
• Strongly push back on a requirement like this
• Refer to e.g. how Google login works in apps, the users want to login with Google
• Integration tests for APIs are an acceptable use
23. 3. Resource Owner Password Credentials
User clicks
login in app
Redirected to
AAD
User logs in
Redirected
back to app
with code
HTTP request
to AAD
AAD returns
tokens for user
Authentication
cookie created
for user in app
User is
authenticated
25. 4. N-tenant app without authorizations
Requirement
• Our Web app needs to offer two logins: one for employees and one for
external people
• We have 2 Azure AD tenants, one for both types
• We can have 2 buttons that the user chooses from in the app
• This is what I call an N-tenant app
• Not a multi-tenant app (any tenant)
• Limited number of tenants allowed
26. 4. N-tenant app without authorizations
Solution that Bob came up with
• We need to support 2 Azure AD tenants, but we can’t specify that in AAD
• So we’ll mark the app as multi-tenant
• The two buttons then specify the tenant id for each tenant in the
authentication redirect to force authentication with that tenant
• Bob tries that he cannot login with a user from another tenant
• For the back-end we use a basic multi-tenant app
27. 4. N-tenant app without authorizations
User clicks login in
app
User clicks either
”Employee” or
”Partner”
Redirected to
tenant-specific
AAD login endpoint
User logs in
Redirected back to
app
Detect user role
based on tenant
used to login
28. 4. N-tenant app without authorizations
The flaw
• A user can modify the authorization URL to sign in with any Azure AD tenant
they want
• Poorly implemented authorization can then lead to problems
• Typical multi-tenant apps turn issuer validation off
29. 4. N-tenant app without authorizations
How to do it better
• Specify the valid tenants as valid token issuers in your back-end
• DO NOT TURN ISSUER VALIDATION OFF
• If you cannot specify a list of valid tenants (maybe it depends on something
runtime), add a validation step after authentication
• Token validation in general is very important in all apps
• Signature, audience, issuer, expiry time, activation time…
30. 4. N-tenant app without authorizations
User clicks login in
app
User selects either
”Employee” or
”Partner”
Redirected to
tenant-specific AAD
login endpoint
User logs in
Redirected back to
app
Validate token
issuer is one of the
N tenants
Detect user role
based on tenant
used to login
32. 5. Secrets in Version Control
Requirement
• We have a service that synchronizes user data from the Employee API to
another data store
• All new developers in the team need to be able to get up and running with
the project quickly
• They should be able to just clone the repo and run it
• Makes sense, having an easy setup is a nice thing to have
33. 5. Secrets in Version Control
Solution that Bob came up with
• Put the client secret for the app in a configuration file and commit it to
version control
• Now every developer gets the secret when they clone the project to their
dev environment, and the app works immediately
34. 5. Secrets in Version Control
Acquire token
with client
credentials
Call Employee
API
35. 5. Secrets in Version Control
The flaw
• Anyone with access to repo also has the app’s password
• Now if the app has application permissions to modify data via e.g. MS Graph
API…
• Audit logs would only show the app’s name and id for any changes
• I probably don’t need to say why this would be worse if the repository is
public?
36. 5. Secrets in Version Control
How to do it better
• Create a group for devs of the project
• Assign group access to an Azure Key Vault
• Add secrets there
• Now new devs only need to be added to the one group
• Use the Azure AppAuthentication library for .NET apps
• Users only need to login and choose account in Visual Studio (Tools / Options / Azure
Service Authentication)
• Can also use AZ CLI
• In Azure you can then use Managed Identity
37. 5. Secrets in Version Control
Acquire token for
Key Vault with
AppAuthentication
library
Get app client
secret from Key
Vault
Acquire token with
client credentials
Call Employee API
39. 6. Using secrets from native apps
Requirement
• A native Windows app needs to call an API, but the user does not have
access to it
• Or we do not want to do login, we just want to get the data from the API
40. 6. Using secrets from native apps
Solution that Bob came up with
• Use Client Credential flow to acquire an access token for the API
• Call API with token
• User does not need to login
41. 6. Using secrets from native apps
The flaw
• Anyone with access to the machine running the app can potentially get the
secret
• You could extract it from the binary or man-in-the-middle attack yourself
42. 6. Using secrets from native apps
How to do it better
• Do not store secrets in native apps
• Mobile / Desktop / Single Page App / React Native etc.
• If the code does not run on your server, it is a native app / public client
• You need to authenticate the user using e.g. Authorization Code flow
• There is also Device Code flow for devices/apps that cannot show the AAD
login screen to the user
• If the API requires authentication, then you have to authenticate the user
44. 7. Not requiring scopes/roles in an API
Requirement
• Make an API that allows our line of business apps access to all employees’
basic data
• There is nothing special about this requirement, this problem may be
something that you should look into in your apps
45. 7. Not requiring scopes/roles in an API
Solution that Bob came up with
• Register API in AAD
• Implement authentication in the API to only accept access tokens from their
AAD tenant
46. 7. Not requiring scopes/roles in an API
The flaw
• An app registered in any tenant can acquire an access token for an app in
any tenant
• The access token will not contain any permissions of course
• But it is valid
• If you do not check for delegated permissions or application permissions in
the token, your API is vulnerable!
47. 7. Not requiring scopes/roles in an API
Get the API’s
identifier from
somewhere
Acquire token
for API from
the API’s tenant
Call API
API validates
token issuer,
audience etc.
Call succeeds
48. 7. Not requiring scopes/roles in an API
How to do it better
• Define scopes exposed by API in Azure Portal
• In an API, always check the token has a valid permission in it
• Apps by default have a user_impersonation scope / delegated permission
• “Access <app-name>”
• Only if created through current app registration experience though
• You can also add more delegated and app permissions to your apps
49. 7. Not requiring scopes/roles in an API
Get the API’s
identifier from
somewhere
Acquire token
for API from
the API’s tenant
Call API
API validates
token issuer,
audience etc.
API validates
token
permissions
Call fails
because it has
no permissions
52. Get the slides and signup for the
Finnish tech community newsletter at
http://aka.ms/td-azure-community
Notas do Editor
az account get-access-token --resource resource-id --subscription name-or-id
Above can be used on other platforms in development environment to utilize AZ CLI, same as what the lib does