Search code examples
oauthmicrosoft-graph-apioutlook-restapiazure-api-apps

Why is Microsoft Oauth2 API changing the scope of authorization requests?


I'm creating an application which needs to request user authorization from a Microsoft Work account. And stumbled into this twice.

At first, I just wanted to read the user e-mail, so I requested the following scopes:

  • User.Read
  • Mail.Read
  • Mail.ReadWrite
  • Mail.ReadWrite.Shared
  • Mail.Read.Shared

However, I kept getting "scope has changed" errors, and noticed that the authorized response from microsoft was automatically including the Mail.Send scope to the requests, even if I didn't request it and it was not present on the authorization web page.

My users don't care about that extra auth, so I just added Mail.Send to my request and moved on. Fine.

I am now required to include the offline_access to the scope list, so I can get Refresh Tokens and keep the app running in background. But when I do it, Microsoft replies me with an authorization request missing the 'offline_access' grant, even if the authorization page showed "Access your data offline" and no errors occur during authentication, yet my Oauth2 flow is broken with a "scope has changed" error:

Scope has changed from "mail.readwrite mail.read.shared mail.read    
mail.readwrite.shared mail.send offline_access user.read" to "mail.readwrite 
mail.read.shared mail.read mail.readwrite.shared mail.send user.read"

So is this a bug in Microsoft Oauth flow, or am I doing anything wrong?

EDIT: I reviewed the application permissions on https://apps.dev.microsoft.com/ and the 'offline_access' scope is not listed there, or any other scope seemingly related. Maybe this means Graph does not yet support that scope, despite it being heavily documented?


Solution

  • I found this issue annoying as well. Read on if you are using Python & oauthlib to process the authentication request -

    In Python 3 and requests_oauthlib library this generates a Warning, rather than an Exception. If you see the source code then you will find these lines in oauthlib/oauth2/rfc6749/parameters.py:

    if not os.environ.get('OAUTHLIB_RELAX_TOKEN_SCOPE', None):
        w = Warning(message)
        w.token = params
        w.old_scope = params.old_scopes
        w.new_scope = params.scopes
        raise w
    

    So all you have to do is set the OAUTHLIB_RELAX_TOKEN_SCOPE variable to True in your environment, to ignore this warning.