Skip to content

Fix macOS Keychain support and M365 scope consistency - #73

Open
arosenfeld2003 wants to merge 2 commits into
MarimerLLC:mainfrom
arosenfeld2003:fix/macos-keychain-and-scope-consistency
Open

Fix macOS Keychain support and M365 scope consistency#73
arosenfeld2003 wants to merge 2 commits into
MarimerLLC:mainfrom
arosenfeld2003:fix/macos-keychain-and-scope-consistency

Conversation

@arosenfeld2003

Copy link
Copy Markdown

Summary

Two related fixes found while setting up an M365 account on macOS with
a least-privilege (Calendars.Read-only) app registration:

  1. macOS Keychain support was never wired up (fixes macOS: M365 token cache throws on first use (missing WithMacKeyChain) #70).
    StorageCreationPropertiesBuilder special-cased Linux
    (WithLinuxUnprotectedFile) but never called WithMacKeyChain(...)
    for macOS, so MSAL's cache helper throws
    Value cannot be null. (Parameter 'keyChainServiceName') on first
    use, despite security.md documenting Keychain-backed storage as
    already implemented.

  2. add-m365-account and M365ProviderService requested different,
    hardcoded scope sets
    (fixes M365: add-m365-account and provider silently request different, hardcoded scopes #71). The CLI always requested
    M365Scopes.WithFiles at login; the provider always requested the
    separate M365Scopes.Default for silent token acquisition at
    runtime. For any app registration granted a narrower permission set
    than either hardcoded default, this mismatch causes every read call
    to silently fail (AcquireTokenSilent can't upgrade scope without
    interactive consent) and return an empty result with no visible
    error — see M365 provider: all read methods swallow exceptions and return empty results #72 for the related silent-failure issue, not addressed
    in this PR to keep the diff focused.

Changes

  • M365AuthenticationService.GetOrCreateAppAsync: add a macOS branch
    calling WithMacKeyChain(serviceName, accountName) alongside the
    existing Linux branch.
  • AddM365AccountCommand: prompt for scopes (default: the previous
    WithFiles set, so existing behavior is unchanged if you accept the
    default), persist the chosen scopes to
    ProviderConfig["Scopes"].
  • M365ProviderService.GetAccessTokenAsync: read
    ProviderConfig["Scopes"] per-account when present, falling back to
    the historical M365Scopes.Default for accounts configured before
    this change (fully backward compatible).
  • OAuthScopes.cs: add a M365Scopes.CalendarsReadOnly convenience
    constant for the calendar-only case.

Testing

Built from source on macOS (arm64, net10.0). Verified end-to-end with
a real Entra app registration scoped to Calendars.Read only:
registered the app, ran add-m365-account with a custom (narrower)
scope, authenticated interactively, and confirmed get_calendar_events
/ list_calendars return real data via the MCP stdio server — prior to
this fix, both silently returned empty results due to the scope
mismatch in issue #71.

Also verified the default (no scopes entered, accept WithFiles
default) path still works unchanged, for backward compatibility with
existing configs that don't have a Scopes key.

Closes #70, #71.

StorageCreationPropertiesBuilder special-cased Linux (unprotected file
storage) but never called WithMacKeyChain() for macOS, so MSAL's
cross-platform cache helper throws "Value cannot be null.
(Parameter 'keyChainServiceName')" on first use on macOS despite the
security docs claiming Keychain-backed storage is already implemented.
…ovider

add-m365-account always requested the hardcoded M365Scopes.WithFiles
set during interactive login, while M365ProviderService always
requested the separate M365Scopes.Default set for silent token
acquisition at runtime. For an app registration granted a narrower set
of permissions (e.g. Calendars.Read only, for least-privilege setups),
this mismatch makes every read call silently fail: AcquireTokenSilent
can't upgrade scope without interactive consent, throws internally,
and every provider method swallows the exception and returns an empty
result with only a log warning - so calendar/email/contact queries
just look like "no data" with no visible error.

Now the CLI prompts for scopes (defaulting to the previous WithFiles
set for backward compatibility) and persists the chosen set on the
account's ProviderConfig. M365ProviderService reads that per-account
Scopes value when present, falling back to the historical default
for existing configs that don't have it set.
@arosenfeld-e14

Copy link
Copy Markdown

@rockfordlhotka appreciate any feedback on this - with these changes I’m set up successfully on mac os (with Claude). Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants