Managed Identity for Azure AppService with Graph API - azure-managed-identity

I need to implement "Managed Identity"
Source: App Service
Destination: Graph API
Type: Managed Identity => User Identity
What are the steps to implement MI for AppService using User Identity, to call Graph API. Need to call Graph API to get user's Security Groups.
I'm using C# and ASP.net MVC. Could you please guide?

Once you have link your user managed identity to the app service (see steps here), you can use it from C# using the Microsoft.Azure.Services.AppAuthentication. You will find examples here (when doing the GetAccessTokenAsync you will need to pass the graph's resource id).
If you want to make a call to access a user information, you will most likely run into permissions issues (the managed identity might not have permissions to access user's info). If it's the case, you should use an application instead of a managed identity to be able to use the On-Behalf Of flow.

Not C#, but for those who are interested in accessing the Microsoft Graph API with managed identities via the #azure/identity Node SDK
import { DefaultAzureCredential } from '#azure/identity';
const authToken = await credential.getToken(['https://graph.microsoft.com/']);
const response = await fetch('https://graph.microsoft.com/v1.0/me', {
headers: { Authorization: authToken.token }
});
console.log(await response.text());

Related

Azure AD - Insufficient privileges to perform requested operation by the application '00000003-0000-0000-c000-000000000000'

I work on a task that should invite users and add them in my azure active directory list. Before being able to access my app, the invited user should verify through email. This is the code I use:
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(_config["AzureAd:ClientId"])
.WithTenantId(_config["AzureAd:TenantId"])
.WithClientSecret(_config["AzureAd:ClientSecret"])
.Build();
ClientCredentialProvider authProvider = new ClientCredentialProvider(confidentialClientApplication);
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
var invitation = new Invitation
{
InvitedUserEmailAddress = "mail#hotmail.com",
InviteRedirectUrl = "https://url.com/",
SendInvitationMessage = true
await graphClient.Invitations
.Request()
.AddAsync(invitation);
I found this snippet somewhere on the internet and judging by the comments, it seems to work. However, when I run my app and call this functionality, I get an error that says
Code: Unauthorized Message: Insufficient privileges to perform requested operation by the application '00000003-0000-0000-c000-000000000000'. ControllerName=MSGraphInviteAPI, ActionName=CreateInvite, URL absolute path=/...
In API permissions, I have a User.Invite.All permission under Microsoft Graph. Besides this I have User.Read as well but I don't think it's relevant for this at the moment. Has some of you stumbled upon an error like this and managed to successfully solve it? If so, would you be kind to share the solution?
You are using client_credentials flow. Which means it is not a User who is performing this task, but rather service credentials. You would need to provide Application permissions, rather than what you have set - Delegated Permissions.
If you don't see Application Permissions, its because you created an Azure AD B2C Application Registration. Rather, create the App Reg with the first option Accounts in this organizational directory only (Contoso only - Single tenant) .
These are the docs you need:
AAD Client Cred flow
MS Graph API daemon app
This is correct method for AAD and AAD B2C tenant today.

Secure Azure Function in Xamarin Forms app.How should it work?

In my xamarin forms app I have the need to call some azure functions in a secure way.
What I have done
All my functions have AuthorizationLevel=Function
Get Function Key by making a call to a webApi that is stored on the server
Function Key is passed in the header of the http call (all works!!)
I do not like the above and definitely I do not want to store the function key on the mobile app as an alternative
I have read about Authentication/Authorization but I cannot figure out if it fits my scenario
My Scenario
Ability to call an azure function in a secure way.User should NOT be prompted to login.Its a silent call.
Is there some sort of accessToken I can use and retrieve safely from
azure portal and use that in some way to access the function?
How do you securely access an azure function in a mobile app?
Any samples? I have read below and did not help
https://learn.microsoft.com/en-us/azure/app-service/overview-authentication-authorization
https://learn.microsoft.com/en-us/azure/app-service/configure-authentication-provider-aad
Edited
Tried below but I get an error(connection string error) in the mobile app.Also not sure how it works when deployed as in debug it uses the credential of the developer
var azureServiceTokenProvider = new AzureServiceTokenProvider();
var keyVaultClient =
new KeyVaultClient(
new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
var secret = await keyVaultClient.GetSecretAsync("mysecretIdentifier").ConfigureAwait(false);
var key = secret.Value;
Recommended Approach
The best way to handle authentication for Azure Functions is to leverage the built-in Authentication and Authorization feature. This uses an existing auth provider to authenticate your users allowing you to avoid creating/storing/maintaining user ids & passwords.
Here's a walkthrough adding Azure AD B2C Authentication to Azure Functions: https://github.com/jimbobbennett/MobileAppsOfTomorrow-Lab/blob/master/Workshop/2-SetupAzureFunctions.md#4-setup-function-app-authentication
Alternative Approach
Since it sounds like you aren't using authentication and you want to have a secure API that only your app can access, we can use AuthorizationLevel=Function and inject the API key into our app using our Continuous Integration server at build-time.
I do this for my GitTrends app. Here's how:
Create a AzureConstants.cs that will store the Functions API Key: https://github.com/brminnick/GitTrends/blob/master/GitTrends.Shared/Constants/AzureConstants.cs
Use git update-index --assume-unchanged AzureConstants.cs to ensure the API Keys aren't committed into source control: https://www.jimbobbennett.io/hiding-api-keys-from-git/
On your Continous Integration Server, Add the API Keys as Environment Variables. Here's how to do it in App Center: https://learn.microsoft.com/appcenter/build/custom/variables/
Add a pre-build script to your Continuous Integration server to inject the API Keys. Here's the pre-build script I use in App Center Build for GitTrends: https://github.com/brminnick/GitTrends/blob/master/GitTrends.iOS/appcenter-pre-build.sh

G Suite identity provider for an AWS driven browser based App

I'm aware of how to create a Google authenticated app via with google-signin-client_id 3089273xx-xxxxxxxxxxxx.apps.googleusercontent.com & <script src="https://apis.google.com/js/platform.js" async defer></script>, but the problem here is that, I have not been able to LIMIT the login to just my company's G Suite instance.
The app I have is a "serverless" JS bundle hosted on S3. The logged in Google token is tied to an AWS role that accesses sensitive resources.
So typical solutions to check the email of googleUser.getBasicProfile() or pass a hd parameter don't make any security sense since they can be manipulated with browser dev tools IIUC.
Is there some other Google API I could be using or strategy I could apply? I imagine the solution would come in the form of a special google-signin-client_id for my company's domain which is hosted by G Suite. This is how it's tied to the role at AWS:
I'm aware I could setup duplicate my users in AWS "user pools" and use Cognito, but I am trying to have a "single source of truth" for the company's employees & ease the administration burden.
UPDATE: This answer is insecure as if you simply remove hosted_domain, you can authenticate with any Google login.
After straying upon https://developers.google.com/identity/work/it-apps & using GAPI directly I found I could do a
GAPI.auth2.init({
client_id: CLIENT_ID,
hosted_domain: 'example.com'
})
And then as the documentation advises, you setup Manage API client access
So now only users of #example.com on Gsuite can access this JS app! This took weeks to figure out. So just to conclude, how to authenticate using Google on a AWS powered serverless app:
Setup a client ID via OAuth client ID with your whitelisted origin URLs from https://console.developers.google.com/apis/credentials
In AWS IAM setup a Role with Google as the (web) Identity provider with the client ID
Add your client ID https://admin.google.com/AdminHome?chromeless=1#OGX:ManageOauthClients as documented here https://developers.google.com/identity/work/it-apps to crucially limit your application to your company's domain.
So now we have a statically hosted App limited to only company employees to access sensitive paid AWS APIs.
I tried 3 different options, the first one worked for my scenario:
First Option - Validating Google Id Token on each call on lambda side
I always pass the id_token as a header on the client calls(web and mobile apps).
"acceptableHds" Is the list of allowed domains.
const oauth = new Auth.OAuth2(CLIENT_ID_WEB, CLIENT_SECRET);
oauth.verifyIdToken(token, null, (err, ticket) => {
if (err) {
return reject(err);
}
const payload = ticket.getPayload();
const tokenIsOK = payload &&
payload.aud === CLIENT_ID &&
new Date(payload.exp * 1000) > new Date() &&
acceptableISSs.has(payload.iss) &&
acceptableHds.has(payload.hd)
return tokenIsOK ? resolve(payload.hd) : reject();
});
Second Option - Validating Google Id Token once on lambda side
I started this alternative way but I didn't finished because the first solutions fitted to my needs and the milestones was close(it needs a indentity pool):
1)Send the id_token to the lambda function and validate it on Google API(here is where you can check the domain using the code above)
2)Call the cognitoidentity.getOpenIdTokenForDeveloperIdentity on the lambda side using the id_token coming from the browser
3) On the client, call any of the Cognito or STS functions like assumeWebIdentity, AssumeRole using the tokens returned from getOpenIdToken.
function getCognitoToken(id_token) {
var param = {
IdentityPoolId: 'us-east-1:f7b3d55f-6b63-4097-be8f-3dc22ddec1a4',
Logins: { 'accounts.google.com': id_token }
}
return check_company(id_token).then(function (valid) {
return cognitoidentity.getOpenIdTokenForDeveloperIdentity(param).promise()
})
I couldn't finish the third step. You need use the tokens received on the second step without revealing the 'identity pool id'. If you do that and assure that the role can't list identity pool ids, it will work as intended and It will be secure.
Third Option - SAML provider
You can create a SAML provider and use SAML assertions to validate the user domain.
http://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_saml_assertions.html
I failed miserably trying to do it.
P.S.: Google Admin let you create private apps, limiting to you company domains, but It works only for mobile as far as I know
https://support.google.com/a/answer/2494992?hl=en
Hope it helps someone!

Where to authenticate user in a SPA?

I have an Aurelia SPA that connects to a ASP.NET Core backend. I use Auth0 for authentication (using aurelia-auth, not the Auth0 Lock widget).
I currently log in with Auth0 directly, not using the SPA. This gives me issues because the Auth0 implementation in my API expects the id_token and not the access_token. This issue can be passed by telling aurelia-auth to use the id_token as access token. But this complicates further communication between Auth0 and the Aurelia app. Auth0 expects the access_token for user profile calls and such.
Should I authenticate via my own API instead? Or should I make two different fetch-clients in Aurelia? One for calling my API (using the id_token) and one for calling the Auth0 API (using the access_token).
I've written a number of blogs on the subject, and I'll link them below for further reading. My recommendation is to create a separate "authentication" root viewModel that is available to all users, distinct from your "app" root viewModel which is available to only logged in users.
main.js
import AuthService from 'AuthService';
export function configure(aurelia) {
aurelia.use
.standardConfiguration()
.developmentLogging();
// After starting the aurelia, we can request the AuthService directly
// from the DI container on the aurelia object. We can then set the
// correct root by querying the AuthService's isAuthenticated method.
aurelia.start().then(() => {
var auth = aurelia.container.get(AuthService);
let root = auth.isAuthenticated() ? 'app' : 'login';
aurelia.setRoot(root);
});
}
Further reading
Aurelia Authentication Best Practices, Multiple Shells
Aurelia Authentication Best Practices, Sessions
Sentry, an Aurelia Authentication Template

Using OAuth tokens to authorize different ASP.NET MVC Applications from Single Sign On Source

I have a number of different web applications, all running in ASP.NET MVC. These ASP.NET MVC applications are .NET/Angular hybrids and generally call into WebAPI APIs which run in their own separate application on IIS.
All applications are on the same domain,but in separate subdomains. They all run on the same instance of IIS.
The structure looks something like this:
Payments Application (MVC, payments.myapp.com)
News Application (MVC, news.myapp.com)
Payments API (WebAPI, paymentsapi.myapp.com)
News API (WebAPI, newsapi.myapp.com)
Login Application (MVC, login.myapp.com)
Login API (WebAPI, loginapi.myapp.com)
What I need to be able to do is log in to the Payments and News Applications from one single source, i.e. the Login Application. The login application then calls into a login API (which is obviously not restricted) to issue a token.
If the user tries to access the Payments or News Applications without a token then they should be redirected to the login application. The Payments API and NewsAPIs should also be inaccessible without a valid token, which should be set at the same time login happens.
Currently, the login API issues a bearer token successfully using OWIN/OAuth - the part I am unsure about is using this across subdomains.
How should I go about doing this?
You can use the Asp.Net built in Owin Authentication middle ware to achieve this functionality. Please refer to this great article and I am sure you will come up with the solution that fits your needs.
Write an extension method on IAppBuilder in a seperate class library and share that across all of your applications for token validation . See the following as an example (code copied from the article):
public static void ConfigureOAuth(this IAppBuilder app)
{
var issuer = "http://jwtauthzsrv.azurewebsites.net";
var audience = "099153c2625149bc8ecb3e85e03f0022";
var secret = TextEncodings.Base64Url.Decode("IxrAjDoa2FqElO7IhrSrUJELhUckePEPVpaePlS_Xaw");
// Api controllers with an [Authorize] attribute will be validated with JWT
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { audience },
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(issuer, secret)
}
});
}
Issuer will issue the token using the same information (secret and audience). Since the validation logic is same across the sub-domains, the token will be valid on all sub-domains.
Please let me know if you have any questions.
Thank you,
Soma.

Resources