cal.com icon indicating copy to clipboard operation
cal.com copied to clipboard

feat: Hanko passkeys support

Open merlinaudio opened this issue 1 year ago • 7 comments

Note: This previously was #13127 but has since been reworked and simplified

What does this PR do?

Fixes https://github.com/calcom/cal.com/issues/13342 Fixes CAL-2984

This PR adds passkey support for logging in through Hanko Passkeys.

Type of change

  • New feature (non-breaking change which adds functionality)
  • This change requires a documentation update (probably)

How should this be tested?

  • Go to cloud.hanko.io and create a "Passkey API" project
    • Then create a new API key set it to HANKO_PASSKEYS_API_KEY="<api key>" (quotes important)
    • Copy the tenant ID to NEXT_PUBLIC_HANKO_PASSKEYS_TENANT_ID=<tenant id>
  • Log in as normal (with password/google/...)
  • Go to settings > passkeys > create a new passkey
  • Log out
  • Go to login > sign in with a passkey

Mandatory Tasks

  • [x] Make sure you have self-reviewed the code. A decent size PR without self-review might be rejected.

next-auth-options.ts

This file now includes a PasskeyProvider.

For its authorize function, I've collected all of the important checks I could find in the email+password provider (a.k.a. CredentialsProvider), such as rate-limiting, user.locked, belongsToActiveTeams, ...) and included them in the PasskeyProvider.

For some of these, the fields/relations returned by UserRepository.findById wasn't enough. Unfortunately .findTeamsByUserId doesn't return enough info either

I didn't want to touch any of the existing queries, so I've added UserRepository.findByIdAndIncludeProfilesAndPassword

...which is the exact same as the email version of this function (findByEmailAndInclude...). It just looks the user up by their ID instead.

Checklist

  • I haven't checked if my PR needs changes to the documentation (unlikely though)
  • I haven't added tests that prove my fix is effective or that my feature works (this is possible with Playwright)

UI

Note about images

These are the same images from the old PR. There's one minor change since then: since icon components aren't supported for Button, EmptyPage, ... anymore, I used Lucide's key-round icon

This is what key-round looks like:

Versus the passkey icon:


Adds passkey button to login page:

CleanShot 2024-01-09 at 17 36 45@2x

Adds "Passkeys" settings section to sidebar:

CleanShot 2024-01-09 at 17 44 38@2x

Adds passkeys settings page

Empty: CleanShot 2024-01-09 at 17 45 45@2x


Clicking on the "add" button and "sign in with a passkey" buttons opens a browser dialog. This looks different for every browser, phone, etc.

For example: CleanShot 2024-01-09 at 17 47 48@2x

With passkeys added: CleanShot 2024-01-09 at 17 49 38@2x

  • The name is set by the Hanko Passkeys API (our backend). We'll improve the name soon so it's not just a semi-random string and more user friendly
  • If the passkey was used, the description says e.g. "Created 3 days ago • Last used 5 minutes ago"

Dropdown stolen straight from API settings page: CleanShot 2024-01-09 at 17 53 13@2x

merlinaudio avatar Apr 25 '24 00:04 merlinaudio

@merlindru is attempting to deploy a commit to the cal Team on Vercel.

A member of the Team first needs to authorize it.

vercel[bot] avatar Apr 25 '24 00:04 vercel[bot]

Thank you for following the naming conventions! 🙏 Feel free to join our discord and post your PR link.

github-actions[bot] avatar Apr 25 '24 00:04 github-actions[bot]

New and removed dependencies detected. Learn more about Socket for GitHub ↗︎

Package New capabilities Transitives Size Publisher
npm/[email protected] None 0 15.1 kB lydell
npm/[email protected] None 0 5.49 kB sindresorhus
npm/[email protected] None 0 27.9 kB benjamn
npm/[email protected] None 0 32.4 MB typescript-bot

🚮 Removed packages: npm/[email protected]

View full report↗︎

socket-security[bot] avatar Apr 25 '24 00:04 socket-security[bot]

We'd really like to use the passkey icon (not part of lucide, it's an SVG)

however the components that made use of it (Button, EmptyPage, ...) have all been changed to only accept a lucide icon name (string)

Is there any way we still could use a custom component as an icon for these?

merlinaudio avatar Apr 25 '24 08:04 merlinaudio

Graphite Automations

"Add foundation team as reviewer" took an action on this PR • (04/25/24)

1 reviewer was added to this PR based on Keith Williams's automation.

"Add consumer team as reviewer" took an action on this PR • (04/25/24)

1 reviewer was added to this PR based on Keith Williams's automation.

graphite-app[bot] avatar Apr 25 '24 08:04 graphite-app[bot]

While PASSKEY_LOGIN_ENABLED hides all passkey related UI on the login page, the settings page as well as the API routes to register and remove passkeys stay enabled

These aren't strictly related to logging in with Passkeys; do you feel like the env var should disable everything related to passkeys or just the ability to login?

merlinaudio avatar Apr 25 '24 08:04 merlinaudio

No dependency changes detected. Learn more about Socket for GitHub ↗︎

👍 No dependency changes detected in pull request

socket-security[bot] avatar May 16 '24 10:05 socket-security[bot]

Thank you @PeerRich and reviewers! If this is it, then I'll make one commit to change the : any to void that Sean mentioned above. I don't think there's anything else to change.

merlinaudio avatar May 16 '24 11:05 merlinaudio

📦 Next.js Bundle Analysis for @calcom/web

This analysis was generated by the Next.js Bundle Analysis action. 🤖

New Page Added

The following page was added to the bundle from the code in this PR:

Page Size (compressed) First Load % of Budget (350 KB)
/settings/security/passkeys 262.11 KB 489.75 KB 139.93%

Eighty-eight Pages Changed Size

The following pages changed size from the code in this PR compared to its base branch:

Page Size (compressed) First Load % of Budget (350 KB)
/apps 276.73 KB 504.37 KB 144.11% (🟢 -2.13%)
/apps/[slug] 294.29 KB 521.93 KB 149.12% (🟢 -2.13%)
/apps/[slug]/[...pages] 586.57 KB 814.21 KB 232.63% (🟢 -1.74%)
/apps/categories 254.06 KB 481.7 KB 137.63% (🟢 -1.85%)
/apps/categories/[category] 258.85 KB 486.5 KB 139.00% (🟢 -2.13%)
/apps/installation/[[...step]] 466.74 KB 694.38 KB 198.39% (🔴 +85.45%)
/apps/installed/[category] 277.97 KB 505.62 KB 144.46% (🟢 -1.89%)
/auth/login 121.14 KB 348.78 KB 99.65% (🟡 +0.62%)
/auth/saml-idp 12.22 KB 239.87 KB 68.53% (🟢 -20.80%)
/availability 426.41 KB 654.05 KB 186.87% (🟢 -1.71%)
/availability/[schedule] 410.28 KB 637.92 KB 182.26% (🟢 -1.72%)
/bookings/[status] 324.07 KB 551.71 KB 157.63% (🟢 -1.75%)
/enterprise 254.12 KB 481.76 KB 137.65% (🟢 -1.86%)
/event-types 559.22 KB 786.87 KB 224.82% (🟢 -1.76%)
/event-types/[type] 435.59 KB 663.24 KB 189.50% (🟢 -1.70%)
/getting-started/[[...step]] 448 KB 675.64 KB 193.04% (🟡 +10.68%)
/insights 474.06 KB 701.7 KB 200.49% (🟢 -1.86%)
/more 253.62 KB 481.27 KB 137.50% (🟢 -1.85%)
/org/[orgSlug] 265.78 KB 493.42 KB 140.98% (🟢 -0.17%)
/org/[orgSlug]/[user] 271.89 KB 499.54 KB 142.72% (🟢 -0.16%)
/org/[orgSlug]/[user]/embed 271.92 KB 499.56 KB 142.73% (🟢 -0.17%)
/org/[orgSlug]/embed 265.79 KB 493.44 KB 140.98% (🟢 -0.16%)
/org/[orgSlug]/team/[slug] 265.78 KB 493.43 KB 140.98% (🟢 -0.17%)
/settings/admin 260.1 KB 487.75 KB 139.36% (🟢 -1.83%)
/settings/admin/apps 273.48 KB 501.13 KB 143.18% (🟢 -1.82%)
/settings/admin/apps/[category] 273.47 KB 501.11 KB 143.17% (🟢 -1.81%)
/settings/admin/flags 263.93 KB 491.57 KB 140.45% (🟢 -1.83%)
/settings/admin/impersonation 260.49 KB 488.14 KB 139.47% (🟢 -1.83%)
/settings/admin/lockedSMS 281.59 KB 509.23 KB 145.49% (🟢 -1.79%)
/settings/admin/oAuth 272.21 KB 499.85 KB 142.82% (🟢 -1.82%)
/settings/admin/orgMigrations/_OrgMigrationLayout 249.03 KB 476.67 KB 136.19% (🟢 -1.82%)
/settings/admin/orgMigrations/moveTeamToOrg 298.8 KB 526.45 KB 150.41% (🟢 -1.80%)
/settings/admin/orgMigrations/moveUserToOrg 318.63 KB 546.28 KB 156.08% (🟢 -1.77%)
/settings/admin/orgMigrations/removeTeamFromOrg 298.57 KB 526.21 KB 150.35% (🟢 -1.80%)
/settings/admin/orgMigrations/removeUserFromOrg 298.58 KB 526.22 KB 150.35% (🟢 -1.80%)
/settings/admin/organizations 261.99 KB 489.63 KB 139.89% (🟢 -1.82%)
/settings/admin/organizations/[id]/edit 260.65 KB 488.29 KB 139.51% (🟢 -1.82%)
/settings/admin/users 262.76 KB 490.4 KB 140.11% (🟢 -1.82%)
/settings/admin/users/[id]/edit 391.96 KB 619.61 KB 177.03% (🟢 -1.79%)
/settings/admin/users/add 391.63 KB 619.27 KB 176.93% (🟢 -1.78%)
/settings/billing 260.31 KB 487.95 KB 139.41% (🟢 -1.82%)
/settings/developer/api-keys 264.7 KB 492.34 KB 140.67% (🟢 -1.83%)
/settings/developer/webhooks 264.87 KB 492.52 KB 140.72% (🟢 -1.83%)
/settings/developer/webhooks/[id] 265.85 KB 493.5 KB 141.00% (🟢 -1.81%)
/settings/developer/webhooks/new 265.88 KB 493.52 KB 141.01% (🟢 -1.81%)
/settings/my-account/appearance 313.32 KB 540.96 KB 154.56% (🟢 -1.82%)
/settings/my-account/calendars 271.7 KB 499.35 KB 142.67% (🟢 -1.87%)
/settings/my-account/conferencing 272.61 KB 500.25 KB 142.93% (🟢 -1.87%)
/settings/my-account/general 375.81 KB 603.45 KB 172.41% (🟢 -1.82%)
/settings/my-account/out-of-office 265.33 KB 492.98 KB 140.85% (🟢 -1.83%)
/settings/my-account/profile 408.24 KB 635.89 KB 181.68% (🟢 -1.82%)
/settings/organizations/[id]/about 159.66 KB 387.3 KB 110.66% (🟡 +0.47%)
/settings/organizations/[id]/add-teams 159.65 KB 387.3 KB 110.66% (🟡 +0.47%)
/settings/organizations/admin-api 260.26 KB 487.9 KB 139.40% (🟢 -1.83%)
/settings/organizations/appearance 122.33 KB 349.97 KB 99.99% (🟢 -48.02%)
/settings/organizations/billing 260.34 KB 487.99 KB 139.42% (🟢 -1.82%)
/settings/organizations/dsync 292.96 KB 520.61 KB 148.75% (🟢 -1.83%)
/settings/organizations/general 348.65 KB 576.3 KB 164.66% (🟢 -1.82%)
/settings/organizations/members 399.83 KB 627.47 KB 179.28% (🟢 -1.82%)
/settings/organizations/new 159.66 KB 387.31 KB 110.66% (🟡 +0.47%)
/settings/organizations/privacy 265.86 KB 493.5 KB 141.00% (🟢 -1.83%)
/settings/organizations/profile 413.12 KB 640.76 KB 183.07% (🟡 +1.72%)
/settings/organizations/sso 270.93 KB 498.58 KB 142.45% (🟢 -1.83%)
/settings/organizations/teams/other 261.17 KB 488.81 KB 139.66% (🟢 -1.82%)
/settings/organizations/teams/other/[id]/appearance 273.07 KB 500.72 KB 143.06% (🟢 -1.78%)
/settings/organizations/teams/other/[id]/members 267.77 KB 495.41 KB 141.55% (🟢 -1.83%)
/settings/organizations/teams/other/[id]/profile 470.57 KB 698.21 KB 199.49% (🟢 -2.08%)
/settings/platform 258.72 KB 486.37 KB 138.96% (🟢 -1.85%)
/settings/platform/new 120.67 KB 348.32 KB 99.52% (🟡 +0.51%)
/settings/platform/oauth-clients/[clientId]/edit 257.03 KB 484.67 KB 138.48% (🟢 -1.85%)
/settings/platform/oauth-clients/create 256.17 KB 483.81 KB 138.23% (🟢 -1.85%)
/settings/security/impersonation 265.51 KB 493.16 KB 140.90% (🟢 -1.82%)
/settings/security/password 303.7 KB 531.34 KB 151.81% (🟢 -1.80%)
/settings/security/sso 270.38 KB 498.03 KB 142.29% (🟢 -1.82%)
/settings/security/two-factor-auth 268.99 KB 496.63 KB 141.90% (🟢 -1.83%)
/settings/teams 259.84 KB 487.48 KB 139.28% (🟢 -1.83%)
/settings/teams/[id]/appearance 273.06 KB 500.7 KB 143.06% (🟢 -1.78%)
/settings/teams/[id]/billing 260.34 KB 487.99 KB 139.42% (🟢 -1.82%)
/settings/teams/[id]/members 377.89 KB 605.53 KB 173.01% (🟢 -1.82%)
/settings/teams/[id]/profile 471.35 KB 698.99 KB 199.71% (🟢 -2.08%)
/settings/teams/new 193 KB 420.64 KB 120.18% (🟢 -1.83%)
/team/[slug] 265.71 KB 493.35 KB 140.96% (🟢 -0.17%)
/team/[slug]/embed 265.78 KB 493.42 KB 140.98% (🟢 -0.17%)
/teams 253.85 KB 481.49 KB 137.57% (🟢 -1.85%)
/upgrade 253.97 KB 481.62 KB 137.60% (🟢 -1.85%)
/video/[uid] 290.26 KB 517.91 KB 147.97% (🟢 -0.44%)
/workflows 285.87 KB 513.52 KB 146.72% (🟢 -1.80%)
/workflows/[workflow] 414.16 KB 641.8 KB 183.37% (🟢 -1.57%)
Details

Only the gzipped size is provided here based on an expert tip.

First Load is the size of the global bundle plus the bundle for the individual page. If a user were to show up to your website and land on a given page, the first load size represents the amount of javascript that user would need to download. If next/link is used, subsequent page loads would only need to download that page's bundle (the number in the "Size" column), since the global bundle has already been downloaded.

Any third party scripts you have added directly to your app using the <script> tag are not accounted for in this analysis

The "Budget %" column shows what percentage of your performance budget the First Load total takes up. For example, if your budget was 100kb, and a given page's first load size was 10kb, it would be 10% of your budget. You can also see how much this has increased or decreased compared to the base branch of your PR. If this percentage has increased by 20% or more, there will be a red status indicator applied, indicating that special attention should be given to this. If you see "+/-

github-actions[bot] avatar May 22 '24 14:05 github-actions[bot]

@merlindru still has a consistently failing e2e test; could you have a looksy?

emrysal avatar May 22 '24 15:05 emrysal

@emrysal Sorry, maybe I'm being dumb, but which e2e test is failing/how can I run it?

With yarn e2e, I don't see anything passkeys related on my end, but there are hundreds of failing tests there, so maybe my setup is borked

Also thanks for staying on top of this :) Much appreciated!

merlinaudio avatar Jun 05 '24 09:06 merlinaudio

This PR is being marked as stale due to inactivity.

github-actions[bot] avatar Jul 02 '24 00:07 github-actions[bot]

All tests seems to be passing @emrysal , can you have a look again ?

Amit91848 avatar Jul 18 '24 12:07 Amit91848

@zomars Can you review https://github.com/calcom/cal.com/pull/15398 and see if we want to go with a solution that uses no external solution?

keithwillcode avatar Jul 24 '24 15:07 keithwillcode

and what are the pros and cons vs in-house without dependency?

PeerRich avatar Jul 24 '24 15:07 PeerRich

@PeerRich one of the major benefits is that you don't need to make any changes to your DB

Also, Hanko maintains the passkey related stuff, so you seldom (if ever) will need to make changes to Cal.com to keep up with new specs/browser changes

merlinaudio avatar Jul 24 '24 16:07 merlinaudio