oama icon indicating copy to clipboard operation
oama copied to clipboard

Can't generate Access/Refresh token for personal Outlook account

Open relma2 opened this issue 9 months ago • 8 comments

I'm trying to replicate how Mailspring generates a Refresh/Access token from Outlook credential, and it looks like they don't use a client secret but rather a "challenge" and this allows personal Outlook accounts.

How do I put a "code challenge" in Oama to replicate this?


To clarify: what I want to do is to be able to authenticate with my personal Outlook/Hotmail account and recieve a refresh/access token from it. My config file looks like this, and I am running in circles:

services
  outlook:
    auth_endpoint: https://login.microsoftonline.com/common/oauth2/v2.0/authorize
    auth_http_method: GET
    auth_params_mode: QueryString
    auth_scope: offline_access
     user.read
    client_id: XXX
    client_secret: XXX # Are we not supposed to use this??

relma2 avatar Feb 27 '25 02:02 relma2

I was just trying to authorize my Hotmail account but it kept giving me an error about an invalid scope. I ultimately changed auth_scope to scope in config.yaml and it worked fine. Maybe that will also solve your problem.

keithbowes avatar Mar 16 '25 18:03 keithbowes

@keithbowes Can you post your config.yaml?

How did you get the redirect_uri working correctly with Microsoft?

AlexanderTheGrey avatar Mar 23 '25 06:03 AlexanderTheGrey

On Sat, Mar 22, 2025 at 11:15:50PM -0700, AlexanderTheGrey wrote:

@keithbowes Can you post your config.yaml?

Instead, both of you, please post the output of the command

oama printenv

pdobsan avatar Mar 23 '25 08:03 pdobsan

Here's mine:

oama printenv
###  Runtime environment  ###
config:
  encryption:
    tag: KEYRING
  services:
    microsoft:
      access_type: null
      auth_endpoint: null
      auth_http_method: null
      auth_params_mode: null
      auth_scope: https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/SMTP.Send
        offline_access
      client_id: 9e5f94bc-e8a4-4e73-b8be-63364c29d753
      client_secret: ''
      prompt: null
      redirect_uri: null
      tenant: common
      token_endpoint: null
      token_http_method: null
      token_params_mode: null
config_file: /root/.config/oama/config.yaml
oama_version: '0.18'
op_sys: |
  Linux pop-os 6.9.3-76060903-generic #202405300957~1718348209~22.04~7817b67 SMP PREEMPT_DYNAMIC Mon J x86_64 x86_64 x86_64 GNU/Linux
options:
  optCommand:
    tag: PrintEnv
  optConfig: ~/.config/oama/config.yaml
  optDebug: false
services:
  microsoft:
    access_type: null
    auth_endpoint: https://login.microsoftonline.com/common/oauth2/v2.0/authorize
    auth_http_method: GET
    auth_params_mode: QueryString
    auth_scope: https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/SMTP.Send
      offline_access
    client_id: 9e5f94bc-e8a4-4e73-b8be-63364c29d753
    client_secret: ''
    prompt: null
    redirect_uri: null
    tenant: common
    token_endpoint: https://login.microsoftonline.com/common/oauth2/v2.0/token
    token_http_method: POST
    token_params_mode: RequestBodyForm
state_dir: /root/.local/state/oama
######

Keep in mind that when I add the redirect_uri used in mutt_oauth2.py:

redirect_uri: https://login.microsoftonline.com/common/oauth2/nativeclient

I get this error:

Authorization to grant OAuth2 access to [email protected] started ... 
oama: Uncaught exception ghc-internal:GHC.Internal.Exception.ErrorCall:

Prelude.read: no parse
oama: Uncaught exception ghc-internal:GHC.Internal.Exception.ErrorCall:

Prelude.read: no parse

AlexanderTheGrey avatar Mar 23 '25 08:03 AlexanderTheGrey

On Sun, Mar 23, 2025 at 01:46:22AM -0700, AlexanderTheGrey wrote:

client_id: 9e5f94bc-e8a4-4e73-b8be-63364c29d753
client_secret: ''

Where does the client_id comes from? That is what kind of client_id this is? Why is the client_secret empty? Your initial report indicated that both fields contained (as they should) some value.

Why are you running oama as root? It is a user space program.

Keep in mind that when I add the redirect_uri used in mutt_oauth2.py:

redirect_uri: https://login.microsoftonline.com/common/oauth2/nativeclient

I get this error:

Admittedly, the error message could have been better here.

As far as oama concerned the redirect_uri is for your browser to tell where to redirect the service provider's reply. That is where oama's built-in http server is listening. As such it should contain a port number.

Anyway, redirect_uri to an external webserver does not make sense here.

pdobsan avatar Mar 23 '25 10:03 pdobsan

The client_id is from Thunderbird. It's essentially a default value, since Microsoft does not provide a default client_id, and Thunderbird's Azure app registration does not require a client_secret.

I'm running oama as root because it's inside of a container.

I'm able to get the OAuth2 token to work on mutt_oauth2.py (with the external redirect_uri that I posted), but I was looking for a more automated solution.

@keithbowes seemed to indicate that they got oama working with an @hotmail.com email domain, so that's why I inquired about it. @relma2 was the initial reporter.

AlexanderTheGrey avatar Mar 23 '25 14:03 AlexanderTheGrey

On Sun, Mar 23, 2025 at 07:00:58AM -0700, AlexanderTheGrey wrote:

The client_id is from Thunderbird. It's essentially a default value, since Microsoft does not provide one, and does not require a client_secret.

I'm running oama as root because it's inside of a container.

I see, that is not really a usage situation I have foreseen. That is somewhat similar to the "remote" usage described at https://github.com/pdobsan/oama#running-oama-remotely

I suppose running oama this way would complicate using the keyring or even gpg.

Where is the browser running during the authorization, inside or outside of the container?

I'm able to get the OAuth2 token to work on mutt_oauth2.py (with the external redirect_uri that I posted), but I was looking

There is no such thing that "OAuth2 token", there are access_token and refresh_token. How did you receive them in this case? Copy/paste from the browser when the authorization completed or the script stores them for you.

For any case, once you have them, in particular the refresh_token you could try to put them into a JSON document. The structure is something like that below:

{
  "access_token": "ya29...",
  "email": {
    "unEmailAddress": ***@***.***"
  },
  "exp_date": "2000-01-01 10:00 UTC",
  "expires_in": 3599,
  "refresh_token": "1//09V...",
  "scope": "https://mail.google.com/",
  "service": "google",
  "token_type": "Bearer"
}

Store this JSON into a keyring entry (oama @.***) or gpg encrypted file. In theory and if you get it right then oama can work with it from that point, starting with an automatic renewal ...

This would need some fiddling and is just a half solution, of course.

@keithbowes seemed to indicate that they got oama working with an @.***` email domain, so that's why I inquired about it.

I am also very curious about that.

pdobsan avatar Mar 23 '25 15:03 pdobsan

Thanks for the clarification. I copy/pasted an initial code from the browser URL provided by mutt_oauth2.py manually, inputted that into mutt_oauth2.py on the terminal, then got the access token.

Apparently there's a way to have the browser automatically follow redirects so you don't need to manually copy/paste the browser URL code to get the access token. However, nobody seems to have figured out how to do that with personal (non-business/corporate) Microsoft emails (@hotmail.com, @outlook.com), since Microsoft removed the ability to easily register an enterprise application and obtain your own client_id and client_secret.

AlexanderTheGrey avatar Mar 23 '25 16:03 AlexanderTheGrey

Upgrade oama and run the authorization with the --device option.

pdobsan avatar Apr 10 '25 10:04 pdobsan

oama: authorizeEmail:
Unknown "Error in $: Failed reading: not a valid json value"
CallStack (from HasCallStack):
  error, called at lib/OAMa/Authorization.hs:491:25 in oama-0.19.0-KcEsBcgBT2n4YdjWjbjUky:OAMa.Authorization

The README documents --device but I had to find the PR (https://github.com/pdobsan/oama/pull/75) to determine that a custom auth_endpoint for the device code flow is necessary. Once I added that it worked with the Thunderbird client ID. Maybe you want to add another field to the services struct for the device code auth endpoint so you can ship the Microsoft one with the app and users can just pass --device?

hpfr avatar Apr 22 '25 23:04 hpfr

As the Readme says, with device flow the only config value one needs is the client_id. In fact, the default auth_endpoint already is https://login.microsoftonline.com/common/oauth2/v2.0/devicecode

As far as I know, the device flow works both for personal and other type of accounts at Microsoft. oama just follows how the Thunderbird's developers deal with Microsoft accounts. See also the output of oama template command.

pdobsan avatar Apr 23 '25 14:04 pdobsan

I consider this issue solved with --device option

pdobsan avatar Apr 23 '25 15:04 pdobsan

I use something like this for outlook:

https://github.com/AdityaGarg8/git-credential-outlook/blob/main/git-credential-outlook

Apparently, on outlook you MUST have https, so I have to use self signed SSL certificates.

AdityaGarg8 avatar Apr 28 '25 13:04 AdityaGarg8