msGraph API from msAccess VBA - Planner plans credentials issue - vba

I am very new to MS Graph and Office 365 and have made good progress. I am an O365 Global Admin for my organisation (a school) and have app development experience. There is a lot of scope for using MS-Access databases in our context for "globally" managing the O365 content. eg contacts, distribution lists and planner tasks. We want to manage these from an on-premises ms-access database or two and with an admin person authenticating the ms-graph activity, ideally.
So, to test, I created a new db and have managed to get it to consume the following endpoint using VBA but with no user authentication for now.
https://graph.microsoft.com/v1.0/groups
However, when I try
https://graph.microsoft.com/v1.0/planner/plans/with my plan id here
I get 401 - Unauthorized: Access is denied due to invalid credentials.
So, clearly my Application registration is wrong or my authentication or both! I have spent hours searching for examples and help and because of the evolving nature of the ecosystem I am finding it pretty hard to work out what I should do now (as opposed to a year or two ago).
The authorisation that generates the access_token that works to allow me access to the groups is:
POST
https://login.microsoftonline.com/{my tenant id here}/oauth2/token
grant_type=client_credentials
client_id={my client id}
client_secret={my url encoded secret} resource=https://graph.microsoft.com
but using that same access_token for the planner tasks throws the 401 error.
My app permissions look like this:
I presume this is because of the difference between the Application and Delegated types but have not fully grasped it all yet. And, I suspect I am using the wrong authentication flow anyway. :-(
So, my questions are:
1. Do my permissions look right?
2. Is my authentication flow correct? Should I be using these instead? ie have I been working from old information?
https://login.microsoftonline.com/{my tenant id here}/oauth2/v2.0/authorize
https://login.microsoftonline.com/{my tenant id here}/oauth2/v2.0/token
As you can tell I have become somewhat confused. If anyone can point me in the right overall direction given what I am attempting that would be so helpful.
Thanks so much,
Murray

1. Do my permissions look right?
Yeah undoubtedly, your azure portal permission seems alright. You need dedicated permission for that also need to grant admin consent which you have done perfectly shown on screen shot.
2. Is my authentication flow correct?
As you are using Client Credentials Grant Flow request format seems alright. But I doubt this flow is suitable for the API you are trying to call. because this API requires dedicated permission.
3. Should I be using these instead?
Since this API need dedicated permission you could use authorization code grant flow.
Follow below steps to get your token using Authorization Code grant flow
Get Authorization Code:
https://login.microsoftonline.com/YourTenant.onmicrosoft.com/oauth2/v2.0/authorize?client_id={ClientId}&response_type=code&redirect_uri={redirectURI}&response_mode=query&scope=https://graph.microsoft.com/.default
Request Token oauth2/V2.0/token with your code:
Request URL: https://login.microsoftonline.com/common/oauth2/V2.0/token Or https://login.microsoftonline.com/YourTenant.onmicrosoft.com/oauth2/V2.0/token
Method: POST
Request Body Format
client_id:Your_Clinet_Id
scope:https://graph.microsoft.com/.default
redirect_uri:Your_Portal_Redirect_URI
grant_type:authorization_code
client_secret:Your_Client_Secret
code: Paste Code Here
Decode Token:
You could decode your token on https://jwt.io/ and make sure you have required permission on your azure portal.
4. Have I been working from old information?
No, Information has no issue so far I have gone through.
Note: For for details implementation of Authorization Code grant flow you could take a look official docs

Related

How to avoid BigQuery refresh token expiry

I am trying to bring the data from GCP Bigquery to Azure data lake using Azure data factory. I was able to setup and was able to bring the data into Azure. But my problem is, the GCP refresh token is keep on expiring.
How to avoid GCP refresh token expiry?
How to generate new refresh token from ADF every time we load the data?
Any help is much appreciated.
I was dealing with the same issue. I Followed the official documentation where it is recommended the tutorial indicated by #Veikko. On this tutorial and during my short research, everyone establish that the refresh_token won't expire. During my test, the refresh_token provided me access during the day. Once the day finished, the access is revoked.
The solution to my issue was to use ServiceAuthentication. The problem is you will require a Self-hosted Integration Runtime. You just need to configure a Service Account on BigQuery. This will provide you and email and a .p12 file that you need to copy on the machine where you installed the integration runtime and done!
My suspect for the reason is that I use multi-factor authentication and VPN to grant access, and it may be the root cause. But it is an open issue for me. Any comment is welcome.
Refresh token should not be expiring. Make sure you are using a refresh token, not an access token. Azure Data Factory will handle internally obtaining and refreshing the access token for you and you will not need to worry about it.
I think that Azure Data Factory BigQuery connector accepts also access token in refresh token -field and I would assume it would behave exactly like in your situation and would not work after access token has expired.
Good documentation on how to authenticate with BigQuery connector can be found here: https://jpda.dev/getting-your-bigquery-refresh-token-for-azure-datafactory-f884ff815a59. In phase Getting our refresh token make sure you use value from key refresh_token, not access_token.
More info on authentication types can be found from Microsoft documentation here: https://learn.microsoft.com/en-us/azure/data-factory/connector-google-bigquery#using-user-authentication
This topic is very confusing given the wide variety of advice especially generalized advice that doesn't apply to Bigquery/GA and ADF! #Veikko would be incorrect in saying it shouldn't expire - Google says differently regarding their tokens.
Refresh token expiration is controlled by the resource and according to Bigquery documentation...
"Access tokens have limited lifetimes. If your application needs access to a Google API beyond the lifetime of a single access token, it can obtain a refresh token. A refresh token allows your application to obtain new access tokens."
I read where this used to support control of the timeout but after Dec 30 2021 it's mandatory at 30 minutes.
Regardless you have to get a refresh token everytime you query Bigquery if you want to use OAuth2.0. I've read a couple of methods of getting the refresh token but are pretty complicated (at least to me). My workaround is same as #lalfab....create hosted IR and store the p12 version of the credentials on the VM referenced by the IR there. It works but of course means maintaining a VM and starting/stopping when needed. I'd love to replace this with OAuth2.0 connection to the Bigquery client id. Would love to find a simple way of doing this. But ADF's bigquery linked server also doesn't support dynamic expression to replace refresh token at connect time so it seems like we need to wait until Microsoft makes the bigquery adapter more intelligent.
Well all of these answers are actually correct.
After a lot of research, I think I found the issue.
The issue is that refresh token DO expire, as long as the project is not published yet. The button to publish it, is under the OAuth consent screen.
Please read: https://developers.google.com/identity/protocols/oauth2#expiration
I havent tested it yet, but have a strong feeling that this is causing the issues for all of us. Hope it helps!

Handling authorization with IdentityServer4

I'm extremely confused on how to use a centralized IDP with both authentication and authorization. The architecture for my project was to be a single web API and one React client. I wanted to keep things structured out into microservices just to try something more modern, but I'm having major issues with the centralized identity, as many others have.
My goal is fairly simple. User logs in, selects a tenant from a list of tenants that they have access to, and then they are redirected to the client with roles and a "tid" or tenant id claim which is just the GUID of the selected company.
The Microsoft prescribed way to add identity in my scenario is IdentityServer, so I started with that. Everything was smooth sailing until I discovered the inner workings of the tokens. While some others have issues adding permissions, the authorization logic in my application is very simple and roles would suffice. While I would initially be fine with roles refreshing naturally via expiration, they must immediately update whenever my users select a different tenant to "log in" to. However, the problem is that I cannot refresh these claims when the user changes tenants without logging out. Essentially, I tried mixing authorization with authentication and hit a wall.
It seems like I have two options:
Obtain the authorization information from a separate provider, or even an endpoint on the identity server itself, like /user-info but for authorization information. This ends up adding a huge overhead, but the actual boilerplate for the server and for the client is minimal. This is similar to how the OSS version of PolicyServer does it, although I do not know how their paid implementation is. My main problem here is that both the client and resource (API) will need this information. How could I avoid N requests per interaction (where N is the number of resources/clients)?
Implement some sort of custom state and keep a store of users who need their JWTs refreshed. Check these and return some custom response to the caller, which then uses custom js client code to refresh the token on this response. This is a huge theory and, even if it is plausible, still introduces state and kind of invalidates the point of JWTs while requiring a large amount of custom code.
So, I apologize for the long post but this is really irking me. I do not NEED to use IdentityServer or JWTs, but I would like to at least have a React front-end. What options do I have for up-to-date tenancy selection and roles? Right when I was willing to give in and implement an authorization endpoint that returns fresh data, I realized I'd be calling it both at the API and client every request. Even with cached data, that's a lot of overhead just in pure http calls. Is there some alternative solution that would work here? Could I honestly just use a cookie with authorization information that is secure and updated only when necessary?
It becomes confusing when you want to use IdentityServer as-is for user authorization. Keep concerns seperated.
As commented by Dominick Baier:
Yes – we recommend to use IdentityServer for end-user authentication,
federation and API access control.
PolicyServer is our recommendation for user authorization.
Option 1 seems the recommended option. So if you decide to go for option 1:
The OSS version of the PolicyServer will suffice for handling the requests. But instead of using a json config file:
// this sets up the PolicyServer client library and policy provider
// - configuration is loaded from appsettings.json
services.AddPolicyServerClient(Configuration.GetSection("Policy"))
.AddAuthorizationPermissionPolicies();
get the information from an endpoint. Add caching to improve performance.
In order to allow centralized access, you can either create a seperate policy server or extend IdentityServer with user authorization endpoints. Use extension grants to access the user authorization endpoints, because you may want to distinguish between client and api.
The json configuration is local. The new endpoint will need it's own data store where it can read the user claims. In order to allow centralized information, add information about where the permissions can be used. Personally I use the scope to model the permissions, because both client and api know the scope.
Final step is to add admin UI or endpoints to maintain the user authorization.
I ended up using remote gRPC calls for the authorization. You can see more at https://github.com/Perustaja/PermissionServerDemo
I don't like to accept my own answer here but I think my solution and thoughts on it in the repository will be good for anyone thinking about possible solutions to handing stale JWT authorization information.

Is JWT right for me?

I've done a fair amount of research on the many different ways to authenticate and authorize users who use my frontend application to access my REST API. I've built a system that uses OAuth2 and JWT and need a sanity check since I'm working on this alone.
For a bit of background, my frontend is built using Vue.js and my API is built using Django with Django Rest Framework. My team is already planning on concurrently developing the mobile and desktop versions of this app which both would require authentication and authorization as well.
To keep things brief, I'll omit the alternative solutions I have and just talk about the most controversial one.
Right now, OAuth2 (my authorization server) grants users a JWT token using ROPC when they provide their email and password to my frontend client. I should note that my API (my resource server) and authorization server live on the same machine.
My application allows users to essentially signup using different plans (for example a free plan and a paid plan). When a user signs up for a free plan, I need the frontend application to not only disable certain features and elements in the UI, but also I need the authorization server and or resource server to limit what that user is allowed to query based on their plan.
The idea is when a user signs up or logs in, my authorization server will get the associated user record from the database and create a valid JWT with a claim attached that states the user's plan and maybe some other non-personal information. Then once signed it sends it off to the user where the frontend can enable/disable parts of the UI... etc. Hence, if a user logs in on mobile, we can customize the UI based on the same claim sent by the JWT.
My issue is that I don't know if this is a good way to go about it. It seems that everyone I've asked in my circle is split on using JWT or not. Those apposed mostly raise security issues, but, when from what I understand, many of the JWT security pitfalls are well documented and can be avoided just using some commonsense as with any other session/token-based authentication. I'm starting to get analysis paralysis. Please help.
CLASSIFICATION
I would say this is really an API Authorization question, as opposed to an OAuth question:
The role of the Authorization Server and tokens is really just to prove the user's identity
Product specific logic comes after the user logs in and is generally best handled in your app
MY PREFERENCES
Here is how I would handle it:
Save the plan type to your product data when the user signs up
After login, look up the user from the access token
Then look up the user's plan type from your product data
Produce a Claims / Principal object in your API containing both
Enforce business rules based on the plan type claim
I would aim for a Claims object something like this:
class ApiClaims {
// The user id in the access token
userId: string;
// The email
email: string;
// The plan type
planType: string;
// Other claims from the token
// Other claims from product data, eg user roles
}
RESOURCES
If interested in this approach, these blog posts of mine may be of interest:
User Data Management
API Authorization
JWT?
You need some kind of API credential that is sent in HTTPS messages and is web and mobile friendly, so I would use JWTs. You could follow the same pattern with any API credential though.
It depends on what you are trying to protect of course, but JWT bearer tokens are an industry standard. Since you control both the client and the authorization server, you can implement it however you like.
I would look into changing Resource Owner Password Credentials flow to authorization code flow. It will enable you to use social authentication providers like Google or Facebook to sign in users (while still maintaining plan info in your own service). Chances are that people trust those companies more to keep their credentials safe than your company, and it allows you to benefit from any authentication features (MFA) those companies implement.
Also, if you want the clients to read the contents of the token, you should use OpenID Connect id_tokens, as those are guarenteed to be in JWT format.

Restricting Azure Identity Providers

I have set up authentication for my application using the Azure Rest API / OAuth 2 flow, following the steps outlined here:
https://ahmetalpbalkan.com/blog/azure-rest-api-with-oauth2/
I have created an ActiveDirectory application within Azure which is linked to an ActiveDirectory instance.
Inside my own application I have configured it to post to the following Azure OAuth endpoint:
https://login.windows.net/<<MY-AD-TENANT-ID>>/oauth2/authorize?client_id=<<GUID>>&response_type=code
This all works fine. I can authenticate against my ActiveDirectory using emails of the form
someuser#<myDomain>.com
However, I have realised that I can also authenticate using any valid microsoft email address, which obviously means that anyone with a valid microsoft email can get an access token for my application e.g.
randomUser#hotmail.com
Can anyone tell me how I can restrict the authentication to just allow users who are in my Active directory? Users with emails of the form
someuser#<myDomain>.com
I have looked through the documentation but have had no luck so far.
Mechanics of Token Validation
What does that really mean: to validate a token? It boils down to three things, really:
Verify that it is well-formed
Verify that it is coming from the intended authority
Verify that it is meant for the current application
Your problem is that you are not doing the number 3 validation.
You probably are missing something like this in your application where you are validating the token:
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Audience = ConfigurationManager.AppSettings["ida:Audience"],
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
});
Currently I have the same problem and trying to figure out a solution.
That's what I found out:
After authentication you get back a JSON Web Token (see this page https://msdn.microsoft.com/en-us/library/azure/dn645542.aspx). After decoding this, there are several information available. But I am not sure which of those could possibly make sure to only allow login of the specified Active Directory.
#Aram refers to the values audience (aud) and tenant (tid). Unfortunately audience is always set to the app_id given with the request and tenant is always set to the tenant-id of the Azure tenant, although you are using a live.com account, for example.
Finally, I came up with the idea of checking for the existence of oid (»Object identifier (ID) of the user object in Azure AD.«, https://msdn.microsoft.com/en-us/library/azure/dn645542.aspx). I hope that this one will only be set if the user is part of the Active Directory that is issuing the authorization.
As a result, I set my app up to do the following: If in the decoded version of the id_token of the Access token response there is no oid property set – the login-request will be rejected.
Problem is: I can't confirm that my approach works, because I don't have a second Azure AD and can't check if only live/hotmail/... users will not be given a oid, but also users from different ADs. Maybe #bobbyr you could try that out and report?
Thanks to Thomas Ebert's prompt I've figured out a way to solve my problem. I don't know if it will help anyone else, but...
Basically when my app gets the token from Azure, before passing it on to the client, I can decode the JWT and just look at the email field.
In my case if the email address isn't one that belongs to my domain I can just send a 401 unauthorized back to the client.
It feels weird that Azure doesn't offer some way of doing this via config, maybe it does, but noone has answered this for me, and I've read enough of their docs now to want to pull my own eyes out so I never see the word Azure again...

Facebook Login without JSSDK, how to get token if already authorized previously

So I am updating an older desktop app (written in VB, .net 4.0) with facebook integration and followed the guide found here, and have been able to successfully get a token (by parsing the uri of the embedded webview if it contains "token="). Now my problem is if I try to login with a facebook account that has already approved the app in a prior session, the webview just gets redirected to https://www.facebook.com/connect/login_success.html without any token information.
Do I HAVE to log all of the tokens I generate manually (ie on successful token generation, I can call their profile info, use their FB ID as key and save the token)? Even if I do, since the email and password is input directly into the facebook login window, how do I check if the user already has a token?
Thanks in advance
The access token can change any time, you need to get it everytime. After getting the token, I immediately get the user information https://graph.facebook.com/me?access_token=??? and use that ID to find their database information.
I couldn't quickly find facebook information but on google's oauth information it says "The access token is also associated with a limited scope that define the kind of data the your client application has access to (for example "Manage your tasks"). An important goal for OAuth 2.0 is to provide secure and convenient access to the protected data, while minimizing the potential impact if an access token is stolen."
https://code.google.com/p/google-api-php-client/wiki/OAuth2
Ok so I finally figured it out myself. My mistake was apparently requesting the access_token directly (ie https://www.facebook.com/dialog/oauth?response_type=token...) to try and save time.
I fixed it by making a request for a 'code' instead (ie https://www.facebook.com/dialog/oauth?response_type=code), which I then use to make a second request to retrieve an access token as documented here: https://developers.facebook.com/docs/facebook-login/login-flow-for-web-no-jssdk/, "Exchanging code for an access token" section a bit lower on the page.
Hope this helps someone in the future, this was very frustrating on my part.
Regards,
Prince