Skip to content

fix(calendar): Microsoft connection replaces existing account when ID token has no email claim #99

Description

@KitKat31337

Before submitting

  • I've searched existing issues and this isn't a duplicate.
  • This is a reproducible bug in Calnode — not a configuration/setup question or a feature request.

What's the bug?

What happened

I connected one Microsoft 365 account to Calnode, then used “Connect another calendar” to connect a second Microsoft 365 account. The second connection replaced the first instead of appearing alongside it for conflict checking.

The Microsoft account picker let me select the second account, and the OAuth flow completed. Both accounts are in the same Entra tenant.

Workaround that resolved it

In the Entra app registration, I went to Token configuration → Add optional claim → ID token → email. After adding that claim and reconnecting the accounts, Calnode kept both connections.

I could not find this requirement in the deployment guide or .env.example.

Possible cause

The Microsoft calendar OAuth request asks for openid, offline_access, and Calendars.ReadWrite, but not profile or email. The connection code identifies accounts using preferred_username from the ID token, falling back to email. Microsoft requires the profile scope for preferred_username, and the email claim is not guaranteed to be present by default.

When both claims are absent, accountEmailFromIDToken returns an empty string. saveToken uses that value to find and delete an existing connection before inserting the new one, so a second account can replace the first.

Relevant code:

Microsoft OAuth scopes and account identification

Microsoft configuration example

Suggested fix

Could the calendar OAuth flow request the identity claims it needs and avoid treating a missing account identifier as a valid deduplication key? A stable identifier such as tenant ID plus object ID would also avoid using a mutable email address as the account key.

Until the code handles this, documenting the ID token email optional claim would help other self-hosted operators connect multiple Microsoft 365 accounts.

Steps to reproduce

  1. Connect one Microsoft 365 account to Calnode
  2. Connect a second Microsoft 365 account.

The second connection replaced the first instead of appearing alongside it for conflict checking.

Calnode version / commit

edge (since 0.9 doesn't pull canEdit properly from MS Graph)

How are you running Calnode?

Self-hosted — Docker

Logs, screenshots, or anything else

No response

Activity

  1. pullfrog commented on Sep 25, 2026

    @pullfrog
    Contributor
  2. shockalotti commented on Sep 26, 2026

    @shockalotti
    Contributor

    @KitKat31337 excellent report - root cause, code pointers, and a verified workaround all in one. Fixed on main:

    • Calnode now requests the profile and email scopes, so preferred_username/email arrive without any Entra token-configuration changes. Your workaround is no longer needed for new connections.
    • If a tenant still sends neither, the account key falls back to the stable tid:oid pair instead of an empty string.
    • And if even that is absent, the connect is refused with an actionable error instead of wiping the other connection. An empty identifier can never be a dedup key again.

    Your second account should survive connecting alongside the first now. Thanks for the precise diagnosis - it made this a small fix.

  3. shockalotti commented on Sep 26, 2026

    @shockalotti
    Contributor

    @KitKat31337 the fix is on main and live in production. I don't have a second Microsoft 365 account here to prove it end to end - but you have the exact setup this bit. Could you verify?

    Either pull main on your Docker deploy and reconnect both accounts, or if you'd rather not touch your instance, say so and I'll find another way to prove it. What I'm looking for: both accounts listed side by side after connecting the second (previously it replaced the first), with no Entra token-configuration changes needed.

  4. KitKat31337 commented on Sep 26, 2026

    @KitKat31337
    ContributorAuthor

    I have some other temp fixes not engineered for proper merging in my local deployment still so will need to spin up a test environment. But I will get it done.

  5. added a commit that references this issue on Sep 29, 2026
  6. KitKat31337 commented on Oct 2, 2026

    @KitKat31337
    ContributorAuthor

    @KitKat31337 the fix is on main and live in production. I don't have a second Microsoft 365 account here to prove it end to end - but you have the exact setup this bit. Could you verify?

    Either pull main on your Docker deploy and reconnect both accounts, or if you'd rather not touch your instance, say so and I'll find another way to prove it. What I'm looking for: both accounts listed side by side after connecting the second (previously it replaced the first), with no Entra token-configuration changes needed.

    This is confirmed fixed on main

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions