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:
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:
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?
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.
The key to this behavior is how the AAD v1 and AAD v2 endpoints handle permissions and consent differently.
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.
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.
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.
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.)