Skip to content

Feature request: add-o365 --headless device-code flow (personal Outlook.com unblock) #356

@adrianshedden

Description

@adrianshedden

Feature request: add-o365 --headless device-code flow

Summary

Add a --headless flag to msgvault add-o365 that uses OAuth 2.0 device-code flow instead of the authorisation-code-with-localhost-callback flow, mirroring the existing add-account --headless for Gmail.

Why

Personal Outlook.com / Hotmail users hit a structural wall in 2026:

  1. IMAP + app password is dead. Microsoft rejected basic auth at 100% as of 30 April 2026 (Exchange Online basic-auth deprecation finished its phased rollout). add-imap against outlook.office365.com:993 now fails with NO AUTHENTICATE failed / NO LOGIN failed even with a valid app password and IMAP enabled at the mailbox level.

  2. Own Azure AD app registration from a personal Microsoft account is no longer possible. As of mid-May 2026 the Azure portal explicitly states: "The ability to create applications outside of a directory has been deprecated. You can get a new directory by joining the M365 Developer Program or signing up for Azure." The M365 Developer Program is now gated to Visual Studio Pro/Enterprise subscribers, and Azure free tier requires a card. Neither is "free as in beer" for a personal user who just wants email archive.

  3. Reusing a known public client_id (e.g. the pnp/cli-microsoft365 CLI's public app 084a3e9f-a9f4-43f7-89f9-d229cf97853e, which is the most defensible reuse since it's an open-source community project) works at the Microsoft /authorize endpoint — the client_id is accepted, scopes including https://outlook.office.com/IMAP.AccessAsUser.All are requested fine — but the redirect URI msgvault hardcodes (http://localhost:8089/callback/microsoft) is rejected with:

    invalid_request: The provided value for the input parameter 'redirect_uri' is not valid. The expected value is a URI which matches a redirect URI registered for this client application.

    Public clients only accept redirect URIs registered against them, and msgvault's port-and-path is not in the m365 CLI app's redirect list (nor in any other public Microsoft OAuth app's list as far as I can tell).

The combination locks personal-account users out of the OAuth path msgvault currently offers.

Why device-code flow fixes it

OAuth 2.0 device-authorisation grant (RFC 8628) doesn't use a redirect_uri at all. The client requests a user_code + verification_uri, displays them to the user, the user visits the URL on any device and enters the code, and the client polls a token endpoint for the result. Microsoft fully supports it on /oauth2/v2.0/devicecode for both personal and work accounts.

Crucially: any public multi-tenant client_id works for device-code flow, because there's no redirect URI to validate against the client's registration.

This is exactly the pattern pnp/cli-microsoft365 uses (m365 login --authType deviceCode), and it's why an unprivileged user on a personal Outlook.com mailbox can authenticate with that CLI without owning a tenant.

Proposed UX

Mirror the existing Gmail flow:

$ msgvault add-o365 user@hotmail.co.uk --headless
Authorizing user@hotmail.co.uk with Microsoft (device code flow)...

To sign in, use a web browser to open:
  https://microsoft.com/devicelogin

Then enter the code:
  GH47PXTC9

Waiting for authorization... (polling every 5 seconds, expires in 15 min)

After the user enters the code in the browser and clicks Allow, msgvault polls until it receives the token, stores it under ~/.msgvault/ with the same on-disk shape as the current flow, and exits with the same success message.

Implementation reference: Microsoft's device code flow docs.

Config tweak

The existing [microsoft] config block stays mostly the same. The only addition is that client_id is no longer required to be a tenant-private app — any public Microsoft client_id that registered the desired scopes works under device-code flow.

For users who don't want to plug a specific client_id, msgvault could ship a default public client_id baked into the binary (analogous to how pnp/cli-microsoft365 ships its 084a3e9f-... default). That removes the last hurdle for personal-account users.

Workaround until shipped

Users without a Microsoft tenant currently have no clean path to msgvault Hotmail/Outlook.com live-sync. Documented workarounds:

  • Defer Hotmail entirely (rely on olm historic archive imports).
  • Sign up for Azure free tier (card needed for identity check) and register a private app.

Both are friction-heavy.

Author note

Built this analysis while wiring my own msgvault deployment (~/.msgvault/ on macOS) against a personal Outlook.com account. Gmail OAuth via add-account worked beautifully reusing an existing Google Cloud OAuth client; the contrast with the Microsoft path is what surfaced this gap. Happy to help test a --headless branch.


Filed by Adrian Shedden — adrian@ai-ops.org — 27 May 2026

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions