In the bot framework OAuth Flow with Teams, what is the correct way to consent to changed permissions? - botframework

I've implemented the OAuth flow in a Teams bot similar to the sample, and I'm able to get a user token and make calls against the graph, but I'm running into an issue with changing permissions. Here's what I did:
Created a bot and gave it mail.read
Add the bot for a user and trigger the OAuth dialog. User is prompted for consent, and signed in.
Successfully read mail
In AAD, added the Mail.ReadWrite and Mail.Send permissions to the app
Next time the OAuthPrompt was triggered, token was returned immediately. No prompt for consent.
Got an access denied exception when trying to write or send mail
So the issue is that changing permissions on the app does not automatically trigger the consent flow again in the OAuthPrompt. I also tried these troubleshooting steps:
I manually signed the user out (botAdapter.SignOutUserAsync) and signed back in, but was not prompted for consent again.
Same as above, but signed out of Teams as well. Launched the web client in a new incognito window, and still no prompt for consent.
Uninstalled and re-installed the bot on the user's account. No change.
Tried the above after waiting a few hours, but still no change.
The only way I found to trigger the consent flow again was to have the user go to https://account.activedirectory.windowsazure.com/r#/applications and delete the consent from there. Even then I had to call botAdapter.SignOutUserAsync (since the bot service still returned a token with the old permissions). Once I did that, and triggered the OAuth prompt again, I was able to get the consent flow with the new permissions to trigger.
So my question is, is there a better way to handle this? If a new version of the bot requires new permissions, shouldn't the OAuthPrompt from Microsoft.Bot.Builder.Dialogs and the bot service handle re-prompting for consent?

Short answer
Bot Framework's auth provider for AAD v1 isn't flexible enough to support adding scopes.
You can switch to the AAD v2 provider and specify your new scopes in the OAuth Connection Settings. Then, if you force the user to sign in again (after SignOutUserAsync), you will get a consent screen including the new scopes. To set up the AAD v2 provider, see the Bot Framework docs on adding authentication to a bot, which has steps for both the AAD v1 and AAD v2 providers.
Long answer
The key to this behavior is how the AAD v1 and AAD v2 endpoints handle permissions and consent differently.
The problem with v1
In v1, the permissions (such as Mail.Send) are pre-registered in the AAD app registration. During sign-in, AAD checks if the user has already consented to any scopes for the app.
If yes, AAD skips the consent screen and eventually provides an access token for the scopes that had already been consented.
If no, AAD shows the consent screen for all registered scopes and eventually provides an access token for all of those scopes.
This explains the behavior you're seeing. After you tell Bot Framework to forget the current access token (via SignOutUserAsync) and force the user to login again, AAD sees that the user has consented previously, so it skips the consent screen and gives you a new token with the old scopes.
Then how do you add permissions for users who have already consented, without forcing the user to delete consent for the app? AAD's login endpoint has an optional prompt parameter that you can set to prompt=consent. This will force AAD to show the consent screen as if the user had not previously consented, and it will contain all registered permissions. So if you try to use an access token and get a 403 Forbidden error (or an equivalent exception), you can take the user through the login flow using prompt=consent.
Unfortunately, with Azure Bot Service's AAD v1 provider, you don't have enough control over the login URL to dynamically set the prompt parameter, so there isn't an easy way to achieve this.
Enter v2
But there's hope! The AAD v2 endpoints have a much more flexible way of adding scopes incrementally. In v2, for delegated permissions, the permissions do not have to be pre-registered in the AAD app registration. Instead, you specify the scopes in the scope parameter of the login URL. During sign-in, AAD checks if the user has consented to the scopes you specified in the URL.
If yes, AAD skips the consent screen and eventually provides an access token for the scopes you specified.
If no, AAD shows the consent screen for all the scopes you specified and eventually provides an access token for all of those scopes.
Either way, you end up with an access token containing all the scopes you specified in the URL.
Bot Framework sets the scope parameter using the scopes you specify in the OAuth Connection Settings. So if you add a new scope there, then the next time the user signs in, they will get a consent screen with the new permissions, and you will get an access token with the new scopes. (Note: To re-trigger the sign-in, you will still have to sign the user out using SignOutUserAsync. Otherwise, Bot Framework will continue giving you the access token it already has, instead of performing a new sign-in flow with AAD.)

Related

Sending automated emails using Gmail API with Java and Oauth authentication

I have a web app which sends emails (gmail) in name of my users
When a user registers, she supplies gmail account and password. Also she has to enable access for Less Secure Apps (I recommend to create a new account for this)
Then I can open a gmail session
session = Session.getInstance(props, new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(user.getEmail(), user.getPassword());
}
});
and send emails on her behalf.
Unfortunately this is going to stop working next 30th May, when Google will allow only OAUTH2 access
I have followed Java Quickstart for Gmail API and I have code up and running for sending emails with OAUTH2: enable gmail api, create an application on google cloud platform, grant send permission, oauth2 client id credential created...
The problem I have is I can't see a way to automatize this task because when creating an authorized credential, a consent screen displays on browser and you have to select the account to be granted manually (maybe because my app in google cloud platform is still pending to be reviewed)
Is there a way to infer the gmail account you want to access from the credentials file (client_secret.json)? Is there a way to automatize this?
No, or yes. It depends.
The whole point of OAuth2 is to improve security by working with authorization tokens rather than asking for user credentials. To do this the user has to consent to the app's access request, and thus the OAuth consent screen cannot be bypassed. This is
explained in Google's documentation. It's not related to your app's review status but rather it's the way OAuth works.
You can still work in a similar way, though . Instead of asking for username and password upon the user's registration you can redirect them to the OAuth consent screen so they can authorize your app. Make sure that your app is requesting offline access type and then you can retrieve an access_token and a refresh_token. These will essentially work as your credentials and you can use the refresh token to generate new access tokens when needed without having the user go through the consent screen each time.
The refresh token doesn't have a "natural" expiration so you can keep using it indefinitely, but there are a few scenarios where it will become invalid, such as it not being used for six months, the user changing passwords (if using Gmail scopes), the user manually revoking access, etc. In these cases you will need to direct them to the consent screen again to reauthorize your app.
In this sense, your app can still work automatically without user input except the initial setup, which you already had to deal with when they supplied you with their credentials. The refresh token expiration can even be compared to what you had to do when the users changed their passwords in your current workflow.
One exception to this are service accounts. If you and your users are part of a Google Workspace domain you can delegate domain-wide access to it, then the service account will be able to access user data without any manual input. Of course, this is because as the domain administrator you pretty much own all the accounts under it. But if you're working with a publicly available application you will have to deal with the limitations I mentioned above.
Sources:
Google's auth overview
Using OAuth 2.0 to access Google APIs
OAuth 2.0 for web applications
The OAuth consent screen

Office365 Exchange REST API errors, endpoints and permissions issues

I am trying to understand the API's that #Microsoft provides. Its just so messy. My goal is to use the Exchange API to edit settings.
So, I have an APP, Access tokens and subscriptions.
When trying to login to https://outlook.office365.com/ I get the following error:
Your subscription is no longer active. Please contact your admin to activate your subscription.
X-ClientId: 05A0A74F610C432EA1AD48C77829580B
request-id 3088673c-f523-4e26-824f-1d0f7cb0a219
X-OWA-Error Microsoft.Exchange.Data.Storage.TenantAccessBlockedException
X-OWA-Version 15.20.2451.30
X-FEServer HE1PR05CA0360
X-BEServer HE1P190MB0508
Date:18/11/2019 09:11:34
But as you can see, I have a subscription:
What I understand is that I have to login to outlook.office365.com before using the API.
Update
Ok, so I can now login to https://outlook.office365.com/mail/inbox. It took a while............. But;
1) I can request: https://outlook.office365.com/api/beta/users/********************************/. It will return the specified user.
2) I can NOT https://outlook.office365.com/api/beta/users/********************************/mailfolders. I tells me 'access denied'. While my app has all Exchange, Office365 permissions. But delegate and application permissions.
The "Grant admin consent for your tenant" feature seems to have been updated yesterday.
However, something is wrong with the new "Grant admin consent for your tenant" feature based on my test.
If you use it to grant admin consent, it will probably not take effect on the back end although admin consent has been successfully completed on Azure Portal.
Currently you could grant admin consent through a URL request.
https://login.microsoftonline.com/{your tenant}/oauth2/authorize?client_id={app id of your Azure AD app}&response_type=code&redirect_uri={redirect uri of your Azure AD app}&nonce=1234&resource=https://outlook.office365.com/&prompt=admin_consent
Please note that you need to create a new Azure AD App currently because if you have used admin consent on Azure Portal, granting admin consent through a URL request will not take effect.

send emails from MY gmail account with OAuth2 and nodemailer

I want to send emails from my gmail address through my own server. I'm using nodemailer and using account credentials is flaky, and often times doesn't work and leads to this thread
I've implemented everything on that thread many times, and still it's flaky, and also I know OAuth2 is the way to go.
I have a project with cliendID and clientSecret in google developer console, as you can see:
But how do I get an access token WITHOUT any browser interaction?
I seem to be missing something trivial here ...
I've went through all google tutorials and docs I could find about OAuth2, tokens, and APIs, but all guides go through the browser in one point.
Go to the OAuth Playground, click the cog on the top right, check the Use your own OAuth credentials and insert your clientID and clientSecret.
Then select the Gmail API v1 scopes you want in the list to the left and follow the outlined steps and you will get an access_token and a refresh_token.
Google Oauth2 actually all Oauth2 implementations I am aware of require that a user grant an application access via a web browser.
There is an alternative type called service accounts this is more like oauth1 service accounts are preauthorized. You can grant a service account access to your google drive by sharing folders and files with the service account like you would any other user. Because they are preauthorized there is no browser window pop up with service accounts.
You can only user service accounts with Gmail if you have a Google domains account Gsuite. The admin can go in and grant the service account access to the Gmail account in question. Perform G Suite Domain-Wide Delegation of Authority
If this is a normal user Gmail account you cant use a service account. You will have to use Oauth2 popup the request and save the refresh token so that you can gain access at a later date.

Sample active-directory-dotnet-webapi-onbehalfof not working

I am trying to run the active-directory-dotnet-webapi-onbehalfof sample in single tenant mode. I've registered the web app service and the native app client as global admin for the tenant. When I run the sample, as a user in the tenant, I can get a token in the client only after consent pops up (first-time for user), and then call makes it to the service, but there it fails with below. Why is that? Documentation says that when a tenant admin registers the application no further consent is needed? I don't think it is possible to give consent when a remote webapi tries to get OBO token. Something seems buggy... AAD team, please clarify how the OBO feature should be used.
AADSTS65001: The user or administrator has not consented to use the application with ID 'b824502e-fe8a-4770-bd98-8d65a07efcc3'. Send an interactive authorization request for this user and resource.
Trace ID: ad7843d0-be4e-4098-8f7c-43c8e5505cfc
Correlation ID: 140466a4-7250-429f-8843-dbd4f63dc60e
Timestamp: 2016-11-25 21:46:13Z
When you register an application using the Azure Management Portal (manage.windowsazure.com) as a tenant administrator, then yes you should not get consent, as long as subsequent tokens are being retrieved in that same tenant.
Because you are seeing consent for the native client app, and then getting that error message, we must assume that something is not being satisfied which would suppress consent for your app.
To resolve this issue, my recommendation is to simply initiate an interactive authorization request just like the error message implies. You can do this by generating a log-in url for your application, with a specific query string that will force prompt tenant wide consent (prompt=admin_consent).
Here is the skeleton for the URL you need to complete:
https://login.microsoftonline.com/<TenantID>/oauth2/authorize?client_id=<AppID>&response_type=code&redirect_uri=<RedirectURI>&resource=<ResourceURI>&prompt=admin_consent
You can do this for all the applications you registered, and you should not run into consent issues (assuming you are indeed signing in as a tenant admin).
I hope this resolves your issue.

Impersonate current user when calling a Google API using service account & delegation of authority

There is a marketplace requirement that if a Google Apps for Work domain admin installs our app for their domain, the admin and any users from their domain should thereafter not see a scope auth screen when accessing our app. The act of installing the app for the domain should implicitly delegate domain-wide authority for the service account associated with our app.
In order to achieve this behavior, I am trying to do delegation of authority to a service account to work on behalf of, AKA impersonate, the currently logged in user.
The code snippet below shows the various attempts that I've made to get this to work. The only one that does work is to pass a domain superuser's email address as the "sub" param (AKA prn) when creating the JWT. However, this essentially elevates a regular run of the mill domain user's privileges to those of super user which is not the desired effect.
var client = new googleapis.auth.JWT(
'<serviceaccount>#developer.gserviceaccount.com',
'localhost.pem',
null,
["https://www.googleapis.com/auth/admin.directory.user.readonly"],
// null - // 403 not auth
// {'userId' : 'domainsuperuser#email.com'} // 403 not auth
// {'userId' : 'me'} // 403 not auth
// "domainsuperuser#email.com" // works!
// "{domainsuperuser#email.com}" // not a valid email error
// 'me' // invalid impersonation prn email address
);
Does Google honor any other ID than just the email address of the person you want to impersonate such as the special 'me' value?
It feels like we are running into a chicken and egg problem here. Essentially we don't want to hardcode the email address (especially not an admin email), so it feels like we have to make an API call. But we can't make an API call without impersonating a user.
You don't need to use a service account and domain-wide delegation in this case. Instead, simply go through the normal OAuth2 flow with the user, and the approval screen will be skipped automatically.
When the admin installs the app and approves your scopes, they are essentially automatically granting you access to those scopes for all users in the domain. And while it is a requirement that users not see the approval screen, you still have to go through the OAuth2 flow with them in order get the OAuth2 token. If you launch an OAuth2 flow for the user, and don't request any scopes not already approved by the domain admin and don't set approval_prompt=force in the URL, then the OAuth2 approval screen will instantly redirect to your redirect URI, making the process invisible to the user.

Resources