@mulverse/mulguard-core 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -0
- package/adapters.d.ts +522 -0
- package/adapters.d.ts.map +1 -0
- package/adapters.js +170 -0
- package/errors.d.ts +429 -0
- package/errors.d.ts.map +1 -0
- package/errors.js +473 -0
- package/index.d.ts +547 -0
- package/index.d.ts.map +1 -0
- package/index.js +142 -0
- package/jwt.d.ts +132 -0
- package/jwt.d.ts.map +1 -0
- package/jwt.js +123 -0
- package/lib/actions/callback/handle-login.d.ts +35 -0
- package/lib/actions/callback/handle-login.d.ts.map +1 -0
- package/lib/actions/callback/handle-login.js +275 -0
- package/lib/actions/callback/index.d.ts +5 -0
- package/lib/actions/callback/index.d.ts.map +1 -0
- package/lib/actions/callback/index.js +409 -0
- package/lib/actions/callback/oauth/callback.d.ts +36 -0
- package/lib/actions/callback/oauth/callback.d.ts.map +1 -0
- package/lib/actions/callback/oauth/callback.js +248 -0
- package/lib/actions/callback/oauth/checks.d.ts +70 -0
- package/lib/actions/callback/oauth/checks.d.ts.map +1 -0
- package/lib/actions/callback/oauth/checks.js +188 -0
- package/lib/actions/callback/oauth/csrf-token.d.ts +33 -0
- package/lib/actions/callback/oauth/csrf-token.d.ts.map +1 -0
- package/lib/actions/callback/oauth/csrf-token.js +39 -0
- package/lib/actions/index.d.ts +6 -0
- package/lib/actions/index.d.ts.map +1 -0
- package/lib/actions/index.js +5 -0
- package/lib/actions/session.d.ts +5 -0
- package/lib/actions/session.d.ts.map +1 -0
- package/lib/actions/session.js +127 -0
- package/lib/actions/signin/authorization-url.d.ts +12 -0
- package/lib/actions/signin/authorization-url.d.ts.map +1 -0
- package/lib/actions/signin/authorization-url.js +94 -0
- package/lib/actions/signin/index.d.ts +4 -0
- package/lib/actions/signin/index.d.ts.map +1 -0
- package/lib/actions/signin/index.js +22 -0
- package/lib/actions/signin/send-token.d.ts +10 -0
- package/lib/actions/signin/send-token.d.ts.map +1 -0
- package/lib/actions/signin/send-token.js +98 -0
- package/lib/actions/signout.d.ts +11 -0
- package/lib/actions/signout.d.ts.map +1 -0
- package/lib/actions/signout.js +30 -0
- package/lib/actions/webauthn-options.d.ts +8 -0
- package/lib/actions/webauthn-options.d.ts.map +1 -0
- package/lib/actions/webauthn-options.js +60 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +70 -0
- package/lib/init.d.ts +25 -0
- package/lib/init.d.ts.map +1 -0
- package/lib/init.js +172 -0
- package/lib/pages/error.d.ts +17 -0
- package/lib/pages/error.d.ts.map +1 -0
- package/lib/pages/error.js +40 -0
- package/lib/pages/index.d.ts +42 -0
- package/lib/pages/index.d.ts.map +1 -0
- package/lib/pages/index.js +136 -0
- package/lib/pages/signin.d.ts +10 -0
- package/lib/pages/signin.d.ts.map +1 -0
- package/lib/pages/signin.js +75 -0
- package/lib/pages/signout.d.ts +8 -0
- package/lib/pages/signout.d.ts.map +1 -0
- package/lib/pages/signout.js +17 -0
- package/lib/pages/styles.d.ts +3 -0
- package/lib/pages/styles.d.ts.map +1 -0
- package/lib/pages/styles.js +381 -0
- package/lib/pages/verify-request.d.ts +8 -0
- package/lib/pages/verify-request.d.ts.map +1 -0
- package/lib/pages/verify-request.js +11 -0
- package/lib/symbols.d.ts +50 -0
- package/lib/symbols.d.ts.map +1 -0
- package/lib/symbols.js +57 -0
- package/lib/utils/actions.d.ts +3 -0
- package/lib/utils/actions.d.ts.map +1 -0
- package/lib/utils/actions.js +14 -0
- package/lib/utils/assert.d.ts +14 -0
- package/lib/utils/assert.d.ts.map +1 -0
- package/lib/utils/assert.js +168 -0
- package/lib/utils/callback-url.d.ts +17 -0
- package/lib/utils/callback-url.d.ts.map +1 -0
- package/lib/utils/callback-url.js +27 -0
- package/lib/utils/cookie.d.ts +111 -0
- package/lib/utils/cookie.d.ts.map +1 -0
- package/lib/utils/cookie.js +205 -0
- package/lib/utils/date.d.ts +7 -0
- package/lib/utils/date.d.ts.map +1 -0
- package/lib/utils/date.js +8 -0
- package/lib/utils/email.d.ts +20 -0
- package/lib/utils/email.d.ts.map +1 -0
- package/lib/utils/email.js +57 -0
- package/lib/utils/env.d.ts +9 -0
- package/lib/utils/env.d.ts.map +1 -0
- package/lib/utils/env.js +96 -0
- package/lib/utils/logger.d.ts +18 -0
- package/lib/utils/logger.d.ts.map +1 -0
- package/lib/utils/logger.js +50 -0
- package/lib/utils/merge.d.ts +3 -0
- package/lib/utils/merge.d.ts.map +1 -0
- package/lib/utils/merge.js +23 -0
- package/lib/utils/providers.d.ts +19 -0
- package/lib/utils/providers.d.ts.map +1 -0
- package/lib/utils/providers.js +149 -0
- package/lib/utils/session.d.ts +7 -0
- package/lib/utils/session.d.ts.map +1 -0
- package/lib/utils/session.js +29 -0
- package/lib/utils/web.d.ts +10 -0
- package/lib/utils/web.d.ts.map +1 -0
- package/lib/utils/web.js +109 -0
- package/lib/utils/webauthn-client.d.ts +30 -0
- package/lib/utils/webauthn-client.d.ts.map +1 -0
- package/lib/utils/webauthn-client.js +197 -0
- package/lib/utils/webauthn-utils.d.ts +81 -0
- package/lib/utils/webauthn-utils.d.ts.map +1 -0
- package/lib/utils/webauthn-utils.js +343 -0
- package/lib/vendored/cookie.d.ts +120 -0
- package/lib/vendored/cookie.d.ts.map +1 -0
- package/lib/vendored/cookie.js +237 -0
- package/package.json +118 -0
- package/providers/42-school.d.ts +240 -0
- package/providers/42-school.d.ts.map +1 -0
- package/providers/42-school.js +78 -0
- package/providers/apple.d.ts +149 -0
- package/providers/apple.d.ts.map +1 -0
- package/providers/apple.js +104 -0
- package/providers/asgardeo.d.ts +102 -0
- package/providers/asgardeo.d.ts.map +1 -0
- package/providers/asgardeo.js +93 -0
- package/providers/atlassian.d.ts +94 -0
- package/providers/atlassian.d.ts.map +1 -0
- package/providers/atlassian.js +84 -0
- package/providers/auth0.d.ts +116 -0
- package/providers/auth0.d.ts.map +1 -0
- package/providers/auth0.js +49 -0
- package/providers/authentik.d.ts +90 -0
- package/providers/authentik.d.ts.map +1 -0
- package/providers/authentik.js +65 -0
- package/providers/azure-ad-b2c.d.ts +104 -0
- package/providers/azure-ad-b2c.d.ts.map +1 -0
- package/providers/azure-ad-b2c.js +100 -0
- package/providers/azure-ad.d.ts +19 -0
- package/providers/azure-ad.d.ts.map +1 -0
- package/providers/azure-ad.js +23 -0
- package/providers/azure-devops.d.ts +128 -0
- package/providers/azure-devops.d.ts.map +1 -0
- package/providers/azure-devops.js +158 -0
- package/providers/bankid-no.d.ts +134 -0
- package/providers/bankid-no.d.ts.map +1 -0
- package/providers/bankid-no.js +65 -0
- package/providers/battlenet.d.ts +85 -0
- package/providers/battlenet.d.ts.map +1 -0
- package/providers/battlenet.js +81 -0
- package/providers/beyondidentity.d.ts +77 -0
- package/providers/beyondidentity.d.ts.map +1 -0
- package/providers/beyondidentity.js +84 -0
- package/providers/bitbucket.d.ts +89 -0
- package/providers/bitbucket.d.ts.map +1 -0
- package/providers/bitbucket.js +92 -0
- package/providers/box.d.ts +63 -0
- package/providers/box.d.ts.map +1 -0
- package/providers/box.js +73 -0
- package/providers/boxyhq-saml.d.ts +121 -0
- package/providers/boxyhq-saml.d.ts.map +1 -0
- package/providers/boxyhq-saml.js +127 -0
- package/providers/bungie.d.ts +167 -0
- package/providers/bungie.d.ts.map +1 -0
- package/providers/bungie.js +174 -0
- package/providers/click-up.d.ts +75 -0
- package/providers/click-up.d.ts.map +1 -0
- package/providers/click-up.js +89 -0
- package/providers/cognito.d.ts +81 -0
- package/providers/cognito.d.ts.map +1 -0
- package/providers/cognito.js +73 -0
- package/providers/coinbase.d.ts +69 -0
- package/providers/coinbase.d.ts.map +1 -0
- package/providers/coinbase.js +78 -0
- package/providers/concept2.d.ts +81 -0
- package/providers/concept2.d.ts.map +1 -0
- package/providers/concept2.js +86 -0
- package/providers/credentials.d.ts +132 -0
- package/providers/credentials.d.ts.map +1 -0
- package/providers/credentials.js +74 -0
- package/providers/descope.d.ts +91 -0
- package/providers/descope.d.ts.map +1 -0
- package/providers/descope.js +78 -0
- package/providers/discord.d.ts +139 -0
- package/providers/discord.d.ts.map +1 -0
- package/providers/discord.js +86 -0
- package/providers/dribbble.d.ts +88 -0
- package/providers/dribbble.d.ts.map +1 -0
- package/providers/dribbble.js +85 -0
- package/providers/dropbox.d.ts +65 -0
- package/providers/dropbox.d.ts.map +1 -0
- package/providers/dropbox.js +88 -0
- package/providers/duende-identity-server6.d.ts +91 -0
- package/providers/duende-identity-server6.d.ts.map +1 -0
- package/providers/duende-identity-server6.js +80 -0
- package/providers/email.d.ts +41 -0
- package/providers/email.d.ts.map +1 -0
- package/providers/email.js +18 -0
- package/providers/eventbrite.d.ts +78 -0
- package/providers/eventbrite.d.ts.map +1 -0
- package/providers/eventbrite.js +88 -0
- package/providers/eveonline.d.ts +94 -0
- package/providers/eveonline.d.ts.map +1 -0
- package/providers/eveonline.js +92 -0
- package/providers/facebook.d.ts +84 -0
- package/providers/facebook.d.ts.map +1 -0
- package/providers/facebook.js +93 -0
- package/providers/faceit.d.ts +64 -0
- package/providers/faceit.d.ts.map +1 -0
- package/providers/faceit.js +74 -0
- package/providers/figma.d.ts +75 -0
- package/providers/figma.d.ts.map +1 -0
- package/providers/figma.js +81 -0
- package/providers/forwardemail.d.ts +4 -0
- package/providers/forwardemail.d.ts.map +1 -0
- package/providers/forwardemail.js +32 -0
- package/providers/foursquare.d.ts +71 -0
- package/providers/foursquare.d.ts.map +1 -0
- package/providers/foursquare.js +91 -0
- package/providers/freshbooks.d.ts +66 -0
- package/providers/freshbooks.d.ts.map +1 -0
- package/providers/freshbooks.js +76 -0
- package/providers/frontegg.d.ts +95 -0
- package/providers/frontegg.d.ts.map +1 -0
- package/providers/frontegg.js +88 -0
- package/providers/fusionauth.d.ts +279 -0
- package/providers/fusionauth.d.ts.map +1 -0
- package/providers/fusionauth.js +292 -0
- package/providers/github.d.ts +127 -0
- package/providers/github.d.ts.map +1 -0
- package/providers/github.js +115 -0
- package/providers/gitlab.d.ts +115 -0
- package/providers/gitlab.d.ts.map +1 -0
- package/providers/gitlab.js +75 -0
- package/providers/google.d.ts +138 -0
- package/providers/google.d.ts.map +1 -0
- package/providers/google.js +119 -0
- package/providers/hubspot.d.ts +76 -0
- package/providers/hubspot.d.ts.map +1 -0
- package/providers/hubspot.js +93 -0
- package/providers/huggingface.d.ts +216 -0
- package/providers/huggingface.d.ts.map +1 -0
- package/providers/huggingface.js +101 -0
- package/providers/identity-server4.d.ts +69 -0
- package/providers/identity-server4.d.ts.map +1 -0
- package/providers/identity-server4.js +64 -0
- package/providers/index.d.ts +61 -0
- package/providers/index.d.ts.map +1 -0
- package/providers/index.js +3 -0
- package/providers/instagram.d.ts +74 -0
- package/providers/instagram.d.ts.map +1 -0
- package/providers/instagram.js +87 -0
- package/providers/kakao.d.ts +148 -0
- package/providers/kakao.d.ts.map +1 -0
- package/providers/kakao.js +103 -0
- package/providers/keycloak.d.ts +100 -0
- package/providers/keycloak.d.ts.map +1 -0
- package/providers/keycloak.js +73 -0
- package/providers/kinde.d.ts +73 -0
- package/providers/kinde.d.ts.map +1 -0
- package/providers/kinde.js +51 -0
- package/providers/line.d.ts +83 -0
- package/providers/line.d.ts.map +1 -0
- package/providers/line.js +73 -0
- package/providers/linkedin.d.ts +77 -0
- package/providers/linkedin.d.ts.map +1 -0
- package/providers/linkedin.js +65 -0
- package/providers/logto.d.ts +98 -0
- package/providers/logto.d.ts.map +1 -0
- package/providers/logto.js +81 -0
- package/providers/loops.d.ts +40 -0
- package/providers/loops.d.ts.map +1 -0
- package/providers/loops.js +59 -0
- package/providers/mailchimp.d.ts +66 -0
- package/providers/mailchimp.d.ts.map +1 -0
- package/providers/mailchimp.js +76 -0
- package/providers/mailgun.d.ts +55 -0
- package/providers/mailgun.d.ts.map +1 -0
- package/providers/mailgun.js +74 -0
- package/providers/mailru.d.ts +63 -0
- package/providers/mailru.d.ts.map +1 -0
- package/providers/mailru.js +61 -0
- package/providers/mastodon.d.ts +90 -0
- package/providers/mastodon.d.ts.map +1 -0
- package/providers/mastodon.js +75 -0
- package/providers/mattermost.d.ts +132 -0
- package/providers/mattermost.d.ts.map +1 -0
- package/providers/mattermost.js +83 -0
- package/providers/medium.d.ts +68 -0
- package/providers/medium.d.ts.map +1 -0
- package/providers/medium.js +84 -0
- package/providers/microsoft-entra-id.d.ts +428 -0
- package/providers/microsoft-entra-id.d.ts.map +1 -0
- package/providers/microsoft-entra-id.js +156 -0
- package/providers/naver.d.ts +80 -0
- package/providers/naver.d.ts.map +1 -0
- package/providers/naver.js +79 -0
- package/providers/netlify.d.ts +66 -0
- package/providers/netlify.d.ts.map +1 -0
- package/providers/netlify.js +85 -0
- package/providers/netsuite.d.ts +189 -0
- package/providers/netsuite.d.ts.map +1 -0
- package/providers/netsuite.js +170 -0
- package/providers/nextcloud.d.ts +150 -0
- package/providers/nextcloud.d.ts.map +1 -0
- package/providers/nextcloud.js +99 -0
- package/providers/nodemailer.d.ts +27 -0
- package/providers/nodemailer.d.ts.map +1 -0
- package/providers/nodemailer.js +34 -0
- package/providers/notion.d.ts +99 -0
- package/providers/notion.d.ts.map +1 -0
- package/providers/notion.js +110 -0
- package/providers/oauth.d.ts +188 -0
- package/providers/oauth.d.ts.map +1 -0
- package/providers/oauth.js +1 -0
- package/providers/okta.d.ts +99 -0
- package/providers/okta.d.ts.map +1 -0
- package/providers/okta.js +63 -0
- package/providers/onelogin.d.ts +65 -0
- package/providers/onelogin.d.ts.map +1 -0
- package/providers/onelogin.js +61 -0
- package/providers/ory-hydra.d.ts +79 -0
- package/providers/ory-hydra.d.ts.map +1 -0
- package/providers/ory-hydra.js +67 -0
- package/providers/osso.d.ts +79 -0
- package/providers/osso.d.ts.map +1 -0
- package/providers/osso.js +77 -0
- package/providers/osu.d.ts +116 -0
- package/providers/osu.d.ts.map +1 -0
- package/providers/osu.js +75 -0
- package/providers/passage.d.ts +88 -0
- package/providers/passage.d.ts.map +1 -0
- package/providers/passage.js +75 -0
- package/providers/passkey.d.ts +65 -0
- package/providers/passkey.d.ts.map +1 -0
- package/providers/passkey.js +87 -0
- package/providers/patreon.d.ts +73 -0
- package/providers/patreon.d.ts.map +1 -0
- package/providers/patreon.js +77 -0
- package/providers/ping-id.d.ts +57 -0
- package/providers/ping-id.d.ts.map +1 -0
- package/providers/ping-id.js +40 -0
- package/providers/pinterest.d.ts +79 -0
- package/providers/pinterest.d.ts.map +1 -0
- package/providers/pinterest.js +85 -0
- package/providers/pipedrive.d.ts +99 -0
- package/providers/pipedrive.d.ts.map +1 -0
- package/providers/pipedrive.js +71 -0
- package/providers/postmark.d.ts +4 -0
- package/providers/postmark.d.ts.map +1 -0
- package/providers/postmark.js +36 -0
- package/providers/provider-types.d.ts +3 -0
- package/providers/provider-types.d.ts.map +1 -0
- package/providers/provider-types.js +1 -0
- package/providers/reddit.d.ts +88 -0
- package/providers/reddit.d.ts.map +1 -0
- package/providers/reddit.js +90 -0
- package/providers/resend.d.ts +4 -0
- package/providers/resend.d.ts.map +1 -0
- package/providers/resend.js +32 -0
- package/providers/roblox.d.ts +67 -0
- package/providers/roblox.d.ts.map +1 -0
- package/providers/roblox.js +53 -0
- package/providers/salesforce.d.ts +59 -0
- package/providers/salesforce.d.ts.map +1 -0
- package/providers/salesforce.js +52 -0
- package/providers/sendgrid.d.ts +4 -0
- package/providers/sendgrid.d.ts.map +1 -0
- package/providers/sendgrid.js +35 -0
- package/providers/simplelogin.d.ts +87 -0
- package/providers/simplelogin.d.ts.map +1 -0
- package/providers/simplelogin.js +83 -0
- package/providers/slack.d.ts +102 -0
- package/providers/slack.d.ts.map +1 -0
- package/providers/slack.js +69 -0
- package/providers/spotify.d.ts +75 -0
- package/providers/spotify.d.ts.map +1 -0
- package/providers/spotify.js +73 -0
- package/providers/strava.d.ts +68 -0
- package/providers/strava.d.ts.map +1 -0
- package/providers/strava.js +80 -0
- package/providers/threads.d.ts +108 -0
- package/providers/threads.d.ts.map +1 -0
- package/providers/threads.js +89 -0
- package/providers/tiktok.d.ts +248 -0
- package/providers/tiktok.d.ts.map +1 -0
- package/providers/tiktok.js +195 -0
- package/providers/todoist.d.ts +76 -0
- package/providers/todoist.d.ts.map +1 -0
- package/providers/todoist.js +97 -0
- package/providers/trakt.d.ts +93 -0
- package/providers/trakt.d.ts.map +1 -0
- package/providers/trakt.js +91 -0
- package/providers/twitch.d.ts +71 -0
- package/providers/twitch.d.ts.map +1 -0
- package/providers/twitch.js +96 -0
- package/providers/twitter.d.ts +183 -0
- package/providers/twitter.d.ts.map +1 -0
- package/providers/twitter.js +100 -0
- package/providers/united-effects.d.ts +80 -0
- package/providers/united-effects.d.ts.map +1 -0
- package/providers/united-effects.js +72 -0
- package/providers/vipps.d.ts +71 -0
- package/providers/vipps.d.ts.map +1 -0
- package/providers/vipps.js +33 -0
- package/providers/vk.d.ts +334 -0
- package/providers/vk.d.ts.map +1 -0
- package/providers/vk.js +103 -0
- package/providers/webauthn.d.ts +148 -0
- package/providers/webauthn.d.ts.map +1 -0
- package/providers/webauthn.js +128 -0
- package/providers/webex.d.ts +78 -0
- package/providers/webex.d.ts.map +1 -0
- package/providers/webex.js +73 -0
- package/providers/wechat.d.ts +78 -0
- package/providers/wechat.d.ts.map +1 -0
- package/providers/wechat.js +105 -0
- package/providers/wikimedia.d.ts +99 -0
- package/providers/wikimedia.d.ts.map +1 -0
- package/providers/wikimedia.js +90 -0
- package/providers/wordpress.d.ts +65 -0
- package/providers/wordpress.d.ts.map +1 -0
- package/providers/wordpress.js +71 -0
- package/providers/workos.d.ts +154 -0
- package/providers/workos.d.ts.map +1 -0
- package/providers/workos.js +143 -0
- package/providers/yandex.d.ts +131 -0
- package/providers/yandex.d.ts.map +1 -0
- package/providers/yandex.js +80 -0
- package/providers/zitadel.d.ts +117 -0
- package/providers/zitadel.d.ts.map +1 -0
- package/providers/zitadel.js +95 -0
- package/providers/zoho.d.ts +63 -0
- package/providers/zoho.d.ts.map +1 -0
- package/providers/zoho.js +79 -0
- package/providers/zoom.d.ts +93 -0
- package/providers/zoom.d.ts.map +1 -0
- package/providers/zoom.js +82 -0
- package/src/adapters/server-actions-helpers.ts +126 -0
- package/src/adapters.ts +603 -0
- package/src/errors.ts +551 -0
- package/src/index.ts +689 -0
- package/src/jwt.ts +283 -0
- package/src/lib/actions/callback/handle-login.ts +334 -0
- package/src/lib/actions/callback/index.ts +554 -0
- package/src/lib/actions/callback/oauth/callback.ts +347 -0
- package/src/lib/actions/callback/oauth/checks.ts +258 -0
- package/src/lib/actions/callback/oauth/csrf-token.ts +60 -0
- package/src/lib/actions/index.ts +5 -0
- package/src/lib/actions/session.ts +167 -0
- package/src/lib/actions/signin/authorization-url.ts +123 -0
- package/src/lib/actions/signin/index.ts +37 -0
- package/src/lib/actions/signin/send-token.ts +124 -0
- package/src/lib/actions/signout.ts +38 -0
- package/src/lib/actions/webauthn-options.ts +100 -0
- package/src/lib/index.ts +97 -0
- package/src/lib/init.ts +236 -0
- package/src/lib/pages/error.tsx +106 -0
- package/src/lib/pages/index.ts +181 -0
- package/src/lib/pages/signin.tsx +255 -0
- package/src/lib/pages/signout.tsx +49 -0
- package/src/lib/pages/styles.css +377 -0
- package/src/lib/pages/styles.ts +381 -0
- package/src/lib/pages/verify-request.tsx +36 -0
- package/src/lib/symbols.ts +60 -0
- package/src/lib/utils/actions.ts +17 -0
- package/src/lib/utils/assert.ts +259 -0
- package/src/lib/utils/callback-url.ts +42 -0
- package/src/lib/utils/cookie.ts +248 -0
- package/src/lib/utils/date.ts +8 -0
- package/src/lib/utils/email.ts +65 -0
- package/src/lib/utils/env.ts +113 -0
- package/src/lib/utils/logger.ts +75 -0
- package/src/lib/utils/merge.ts +30 -0
- package/src/lib/utils/providers.ts +203 -0
- package/src/lib/utils/session.ts +41 -0
- package/src/lib/utils/web.ts +151 -0
- package/src/lib/utils/webauthn-client.js +229 -0
- package/src/lib/utils/webauthn-utils.ts +531 -0
- package/src/lib/vendored/cookie.ts +383 -0
- package/src/providers/42-school.ts +256 -0
- package/src/providers/apple.ts +206 -0
- package/src/providers/asgardeo.ts +118 -0
- package/src/providers/atlassian.ts +120 -0
- package/src/providers/auth0.ts +127 -0
- package/src/providers/authentik.ts +100 -0
- package/src/providers/azure-ad-b2c.ts +124 -0
- package/src/providers/azure-ad.ts +30 -0
- package/src/providers/azure-devops.ts +184 -0
- package/src/providers/bankid-no.ts +161 -0
- package/src/providers/battlenet.ts +107 -0
- package/src/providers/beyondidentity.ts +102 -0
- package/src/providers/bitbucket.ts +122 -0
- package/src/providers/box.ts +87 -0
- package/src/providers/boxyhq-saml.ts +148 -0
- package/src/providers/bungie.ts +192 -0
- package/src/providers/click-up.ts +104 -0
- package/src/providers/cognito.ts +94 -0
- package/src/providers/coinbase.ts +93 -0
- package/src/providers/concept2.ts +108 -0
- package/src/providers/credentials.ts +157 -0
- package/src/providers/descope.ts +105 -0
- package/src/providers/discord.ts +176 -0
- package/src/providers/dribbble.ts +122 -0
- package/src/providers/dropbox.ts +102 -0
- package/src/providers/duende-identity-server6.ts +101 -0
- package/src/providers/email.ts +60 -0
- package/src/providers/eventbrite.ts +105 -0
- package/src/providers/eveonline.ts +117 -0
- package/src/providers/facebook.ts +119 -0
- package/src/providers/faceit.ts +90 -0
- package/src/providers/figma.ts +105 -0
- package/src/providers/forwardemail.ts +37 -0
- package/src/providers/foursquare.ts +105 -0
- package/src/providers/freshbooks.ts +90 -0
- package/src/providers/frontegg.ts +111 -0
- package/src/providers/fusionauth.ts +336 -0
- package/src/providers/github.ts +187 -0
- package/src/providers/gitlab.ts +140 -0
- package/src/providers/google.ts +152 -0
- package/src/providers/hubspot.ts +117 -0
- package/src/providers/huggingface.ts +234 -0
- package/src/providers/identity-server4.ts +78 -0
- package/src/providers/index.ts +115 -0
- package/src/providers/instagram.ts +103 -0
- package/src/providers/kakao.ts +184 -0
- package/src/providers/keycloak.ts +111 -0
- package/src/providers/kinde.ts +85 -0
- package/src/providers/line.ts +99 -0
- package/src/providers/linkedin.ts +91 -0
- package/src/providers/logto.ts +122 -0
- package/src/providers/loops.ts +79 -0
- package/src/providers/mailchimp.ts +90 -0
- package/src/providers/mailgun.ts +98 -0
- package/src/providers/mailru.ts +75 -0
- package/src/providers/mastodon.ts +112 -0
- package/src/providers/mattermost.ts +154 -0
- package/src/providers/medium.ts +89 -0
- package/src/providers/microsoft-entra-id.ts +497 -0
- package/src/providers/naver.ts +102 -0
- package/src/providers/netlify.ts +90 -0
- package/src/providers/netsuite.ts +225 -0
- package/src/providers/nextcloud.ts +207 -0
- package/src/providers/nodemailer.ts +84 -0
- package/src/providers/notion.ts +166 -0
- package/src/providers/oauth.ts +310 -0
- package/src/providers/okta.ts +111 -0
- package/src/providers/onelogin.ts +75 -0
- package/src/providers/ory-hydra.ts +93 -0
- package/src/providers/osso.ts +91 -0
- package/src/providers/osu.ts +138 -0
- package/src/providers/passage.ts +103 -0
- package/src/providers/passkey.ts +94 -0
- package/src/providers/patreon.ts +98 -0
- package/src/providers/ping-id.ts +68 -0
- package/src/providers/pinterest.ts +106 -0
- package/src/providers/pipedrive.ts +120 -0
- package/src/providers/postmark.ts +38 -0
- package/src/providers/provider-types.ts +107 -0
- package/src/providers/reddit.ts +104 -0
- package/src/providers/resend.ts +35 -0
- package/src/providers/roblox.ts +94 -0
- package/src/providers/salesforce.ts +73 -0
- package/src/providers/sendgrid.ts +36 -0
- package/src/providers/simplelogin.ts +107 -0
- package/src/providers/slack.ts +115 -0
- package/src/providers/spotify.ts +99 -0
- package/src/providers/strava.ts +101 -0
- package/src/providers/threads.ts +135 -0
- package/src/providers/tiktok.ts +319 -0
- package/src/providers/todoist.ts +122 -0
- package/src/providers/trakt.ts +120 -0
- package/src/providers/twitch.ts +121 -0
- package/src/providers/twitter.ts +207 -0
- package/src/providers/united-effects.ts +89 -0
- package/src/providers/vipps.ts +86 -0
- package/src/providers/vk.ts +401 -0
- package/src/providers/webauthn.ts +296 -0
- package/src/providers/webex.ts +102 -0
- package/src/providers/wechat.ts +141 -0
- package/src/providers/wikimedia.ts +258 -0
- package/src/providers/wordpress.ts +86 -0
- package/src/providers/workos.ts +180 -0
- package/src/providers/yandex.ts +159 -0
- package/src/providers/zitadel.ts +128 -0
- package/src/providers/zoho.ts +84 -0
- package/src/providers/zoom.ts +119 -0
- package/src/types.ts +430 -0
- package/src/warnings.ts +21 -0
- package/types.d.ts +309 -0
- package/types.d.ts.map +1 -0
- package/types.js +53 -0
- package/warnings.d.ts +17 -0
- package/warnings.d.ts.map +1 -0
- package/warnings.js +1 -0
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
import * as checks from "./checks.js"
|
|
2
|
+
import * as o from "oauth4webapi"
|
|
3
|
+
import {
|
|
4
|
+
OAuthCallbackError,
|
|
5
|
+
OAuthProfileParseError,
|
|
6
|
+
} from "../../../../errors.js"
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
Account,
|
|
10
|
+
InternalOptions,
|
|
11
|
+
LoggerInstance,
|
|
12
|
+
Profile,
|
|
13
|
+
RequestInternal,
|
|
14
|
+
TokenSet,
|
|
15
|
+
User,
|
|
16
|
+
} from "../../../../types.js"
|
|
17
|
+
import { type OAuthConfigInternal } from "../../../../providers/index.js"
|
|
18
|
+
import type { Cookie } from "../../../utils/cookie.js"
|
|
19
|
+
import { isOIDCProvider } from "../../../utils/providers.js"
|
|
20
|
+
import { conformInternal, customFetch } from "../../../symbols.js"
|
|
21
|
+
import { decodeJwt } from "jose"
|
|
22
|
+
|
|
23
|
+
function formUrlEncode(token: string) {
|
|
24
|
+
return encodeURIComponent(token).replace(/%20/g, "+")
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Formats client_id and client_secret as an HTTP Basic Authentication header as per the OAuth 2.0
|
|
29
|
+
* specified in RFC6749.
|
|
30
|
+
*/
|
|
31
|
+
function clientSecretBasic(clientId: string, clientSecret: string) {
|
|
32
|
+
const username = formUrlEncode(clientId)
|
|
33
|
+
const password = formUrlEncode(clientSecret)
|
|
34
|
+
const credentials = btoa(`${username}:${password}`)
|
|
35
|
+
return `Basic ${credentials}`
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Handles the following OAuth steps.
|
|
40
|
+
* https://www.rfc-editor.org/rfc/rfc6749#section-4.1.1
|
|
41
|
+
* https://www.rfc-editor.org/rfc/rfc6749#section-4.1.3
|
|
42
|
+
* https://openid.net/specs/openid-connect-core-1_0.html#UserInfoRequest
|
|
43
|
+
*
|
|
44
|
+
* @note Although requesting userinfo is not required by the OAuth2.0 spec,
|
|
45
|
+
* we fetch it anyway. This is because we always want a user profile.
|
|
46
|
+
*/
|
|
47
|
+
export async function handleOAuth(
|
|
48
|
+
params: RequestInternal["query"],
|
|
49
|
+
cookies: RequestInternal["cookies"],
|
|
50
|
+
options: InternalOptions<"oauth" | "oidc">
|
|
51
|
+
) {
|
|
52
|
+
const { logger, provider } = options
|
|
53
|
+
|
|
54
|
+
let as: o.AuthorizationServer
|
|
55
|
+
|
|
56
|
+
const { token, userinfo } = provider
|
|
57
|
+
// Falls back to authjs.dev if the user only passed params
|
|
58
|
+
if (
|
|
59
|
+
(!token?.url || token.url.host === "authjs.dev") &&
|
|
60
|
+
(!userinfo?.url || userinfo.url.host === "authjs.dev")
|
|
61
|
+
) {
|
|
62
|
+
// We assume that issuer is always defined as this has been asserted earlier
|
|
63
|
+
|
|
64
|
+
const issuer = new URL(provider.issuer!)
|
|
65
|
+
const discoveryResponse = await o.discoveryRequest(issuer, {
|
|
66
|
+
[o.allowInsecureRequests]: true,
|
|
67
|
+
[o.customFetch]: provider[customFetch],
|
|
68
|
+
})
|
|
69
|
+
as = await o.processDiscoveryResponse(issuer, discoveryResponse)
|
|
70
|
+
|
|
71
|
+
if (!as.token_endpoint)
|
|
72
|
+
throw new TypeError(
|
|
73
|
+
"TODO: Authorization server did not provide a token endpoint."
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
if (!as.userinfo_endpoint)
|
|
77
|
+
throw new TypeError(
|
|
78
|
+
"TODO: Authorization server did not provide a userinfo endpoint."
|
|
79
|
+
)
|
|
80
|
+
} else {
|
|
81
|
+
as = {
|
|
82
|
+
issuer: provider.issuer ?? "https://authjs.dev", // TODO: review fallback issuer
|
|
83
|
+
token_endpoint: token?.url.toString(),
|
|
84
|
+
userinfo_endpoint: userinfo?.url.toString(),
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const client: o.Client = {
|
|
89
|
+
client_id: provider.clientId,
|
|
90
|
+
...provider.client,
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
let clientAuth: o.ClientAuth
|
|
94
|
+
|
|
95
|
+
switch (client.token_endpoint_auth_method) {
|
|
96
|
+
// TODO: in the next breaking major version have undefined be `client_secret_post`
|
|
97
|
+
case undefined:
|
|
98
|
+
case "client_secret_basic":
|
|
99
|
+
// TODO: in the next breaking major version use o.ClientSecretBasic() here
|
|
100
|
+
clientAuth = (_as, _client, _body, headers) => {
|
|
101
|
+
headers.set(
|
|
102
|
+
"authorization",
|
|
103
|
+
clientSecretBasic(provider.clientId, provider.clientSecret!)
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
break
|
|
107
|
+
case "client_secret_post":
|
|
108
|
+
clientAuth = o.ClientSecretPost(provider.clientSecret!)
|
|
109
|
+
break
|
|
110
|
+
case "client_secret_jwt":
|
|
111
|
+
clientAuth = o.ClientSecretJwt(provider.clientSecret!)
|
|
112
|
+
break
|
|
113
|
+
case "private_key_jwt":
|
|
114
|
+
clientAuth = o.PrivateKeyJwt(provider.token!.clientPrivateKey!, {
|
|
115
|
+
// TODO: review in the next breaking change
|
|
116
|
+
[o.modifyAssertion](_header, payload) {
|
|
117
|
+
payload.aud = [as.issuer, as.token_endpoint!]
|
|
118
|
+
},
|
|
119
|
+
})
|
|
120
|
+
break
|
|
121
|
+
case "none":
|
|
122
|
+
clientAuth = o.None()
|
|
123
|
+
break
|
|
124
|
+
default:
|
|
125
|
+
throw new Error("unsupported client authentication method")
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const resCookies: Cookie[] = []
|
|
129
|
+
|
|
130
|
+
const state = await checks.state.use(cookies, resCookies, options)
|
|
131
|
+
|
|
132
|
+
let codeGrantParams: URLSearchParams
|
|
133
|
+
try {
|
|
134
|
+
codeGrantParams = o.validateAuthResponse(
|
|
135
|
+
as,
|
|
136
|
+
client,
|
|
137
|
+
new URLSearchParams(params),
|
|
138
|
+
provider.checks.includes("state") ? state : o.skipStateCheck
|
|
139
|
+
)
|
|
140
|
+
} catch (err) {
|
|
141
|
+
if (err instanceof o.AuthorizationResponseError) {
|
|
142
|
+
const cause = {
|
|
143
|
+
providerId: provider.id,
|
|
144
|
+
...Object.fromEntries(err.cause.entries()),
|
|
145
|
+
}
|
|
146
|
+
logger.debug("OAuthCallbackError", cause)
|
|
147
|
+
throw new OAuthCallbackError("OAuth Provider returned an error", cause)
|
|
148
|
+
}
|
|
149
|
+
throw err
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const codeVerifier = await checks.pkce.use(cookies, resCookies, options)
|
|
153
|
+
|
|
154
|
+
let redirect_uri = provider.callbackUrl
|
|
155
|
+
if (!options.isOnRedirectProxy && provider.redirectProxyUrl) {
|
|
156
|
+
redirect_uri = provider.redirectProxyUrl
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
let codeGrantResponse = await o.authorizationCodeGrantRequest(
|
|
160
|
+
as,
|
|
161
|
+
client,
|
|
162
|
+
clientAuth,
|
|
163
|
+
codeGrantParams,
|
|
164
|
+
redirect_uri,
|
|
165
|
+
codeVerifier ?? "decoy",
|
|
166
|
+
{
|
|
167
|
+
// TODO: move away from allowing insecure HTTP requests
|
|
168
|
+
[o.allowInsecureRequests]: true,
|
|
169
|
+
[o.customFetch]: (...args) => {
|
|
170
|
+
if (!provider.checks.includes("pkce")) {
|
|
171
|
+
args[1].body.delete("code_verifier")
|
|
172
|
+
}
|
|
173
|
+
return (provider[customFetch] ?? fetch)(...args)
|
|
174
|
+
},
|
|
175
|
+
}
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
if (provider.token?.conform) {
|
|
179
|
+
codeGrantResponse =
|
|
180
|
+
(await provider.token.conform(codeGrantResponse.clone())) ??
|
|
181
|
+
codeGrantResponse
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
let profile: Profile = {}
|
|
185
|
+
|
|
186
|
+
const requireIdToken = isOIDCProvider(provider)
|
|
187
|
+
|
|
188
|
+
if (provider[conformInternal]) {
|
|
189
|
+
switch (provider.id) {
|
|
190
|
+
case "microsoft-entra-id":
|
|
191
|
+
case "azure-ad": {
|
|
192
|
+
/**
|
|
193
|
+
* These providers return errors in the response body and
|
|
194
|
+
* need the authorization server metadata to be re-processed
|
|
195
|
+
* based on the `id_token`'s `tid` claim.
|
|
196
|
+
* @see: https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-auth-code-flow#error-response-1
|
|
197
|
+
*/
|
|
198
|
+
const responseJson = await codeGrantResponse.clone().json()
|
|
199
|
+
if (responseJson.error) {
|
|
200
|
+
const cause = {
|
|
201
|
+
providerId: provider.id,
|
|
202
|
+
...responseJson,
|
|
203
|
+
}
|
|
204
|
+
throw new OAuthCallbackError(
|
|
205
|
+
`OAuth Provider returned an error: ${responseJson.error}`,
|
|
206
|
+
cause
|
|
207
|
+
)
|
|
208
|
+
}
|
|
209
|
+
const { tid } = decodeJwt(responseJson.id_token)
|
|
210
|
+
if (typeof tid === "string") {
|
|
211
|
+
const tenantRe = /microsoftonline\.com\/(\w+)\/v2\.0/
|
|
212
|
+
const tenantId = as.issuer?.match(tenantRe)?.[1] ?? "common"
|
|
213
|
+
const issuer = new URL(as.issuer.replace(tenantId, tid))
|
|
214
|
+
const discoveryResponse = await o.discoveryRequest(issuer, {
|
|
215
|
+
[o.customFetch]: provider[customFetch],
|
|
216
|
+
})
|
|
217
|
+
as = await o.processDiscoveryResponse(issuer, discoveryResponse)
|
|
218
|
+
}
|
|
219
|
+
break
|
|
220
|
+
}
|
|
221
|
+
default:
|
|
222
|
+
break
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
const processedCodeResponse = await o.processAuthorizationCodeResponse(
|
|
226
|
+
as,
|
|
227
|
+
client,
|
|
228
|
+
codeGrantResponse,
|
|
229
|
+
{
|
|
230
|
+
expectedNonce: await checks.nonce.use(cookies, resCookies, options),
|
|
231
|
+
requireIdToken,
|
|
232
|
+
}
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
const tokens: TokenSet & Pick<Account, "expires_at"> = processedCodeResponse
|
|
236
|
+
|
|
237
|
+
if (requireIdToken) {
|
|
238
|
+
const idTokenClaims = o.getValidatedIdTokenClaims(processedCodeResponse)!
|
|
239
|
+
profile = idTokenClaims
|
|
240
|
+
|
|
241
|
+
// Apple sends some of the user information in a `user` parameter as a stringified JSON.
|
|
242
|
+
// It also only does so the first time the user consents to share their information.
|
|
243
|
+
if (provider[conformInternal] && provider.id === "apple") {
|
|
244
|
+
try {
|
|
245
|
+
profile.user = JSON.parse(params?.user)
|
|
246
|
+
} catch {}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (provider.idToken === false) {
|
|
250
|
+
const userinfoResponse = await o.userInfoRequest(
|
|
251
|
+
as,
|
|
252
|
+
client,
|
|
253
|
+
processedCodeResponse.access_token,
|
|
254
|
+
{
|
|
255
|
+
[o.customFetch]: provider[customFetch],
|
|
256
|
+
// TODO: move away from allowing insecure HTTP requests
|
|
257
|
+
[o.allowInsecureRequests]: true,
|
|
258
|
+
}
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
profile = await o.processUserInfoResponse(
|
|
262
|
+
as,
|
|
263
|
+
client,
|
|
264
|
+
idTokenClaims.sub,
|
|
265
|
+
userinfoResponse
|
|
266
|
+
)
|
|
267
|
+
}
|
|
268
|
+
} else {
|
|
269
|
+
if (userinfo?.request) {
|
|
270
|
+
const _profile = await userinfo.request({ tokens, provider })
|
|
271
|
+
if (_profile instanceof Object) profile = _profile
|
|
272
|
+
} else if (userinfo?.url) {
|
|
273
|
+
const userinfoResponse = await o.userInfoRequest(
|
|
274
|
+
as,
|
|
275
|
+
client,
|
|
276
|
+
processedCodeResponse.access_token,
|
|
277
|
+
{
|
|
278
|
+
[o.customFetch]: provider[customFetch],
|
|
279
|
+
// TODO: move away from allowing insecure HTTP requests
|
|
280
|
+
[o.allowInsecureRequests]: true,
|
|
281
|
+
}
|
|
282
|
+
)
|
|
283
|
+
profile = await userinfoResponse.json()
|
|
284
|
+
} else {
|
|
285
|
+
throw new TypeError("No userinfo endpoint configured")
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (tokens.expires_in) {
|
|
290
|
+
tokens.expires_at =
|
|
291
|
+
Math.floor(Date.now() / 1000) + Number(tokens.expires_in)
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const profileResult = await getUserAndAccount(
|
|
295
|
+
profile,
|
|
296
|
+
provider,
|
|
297
|
+
tokens,
|
|
298
|
+
logger
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
return { ...profileResult, profile, cookies: resCookies }
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Returns the user and account that is going to be created in the database.
|
|
306
|
+
* @internal
|
|
307
|
+
*/
|
|
308
|
+
export async function getUserAndAccount(
|
|
309
|
+
OAuthProfile: Profile,
|
|
310
|
+
provider: OAuthConfigInternal<any>,
|
|
311
|
+
tokens: TokenSet,
|
|
312
|
+
logger: LoggerInstance
|
|
313
|
+
) {
|
|
314
|
+
try {
|
|
315
|
+
const userFromProfile = await provider.profile(OAuthProfile, tokens)
|
|
316
|
+
const user = {
|
|
317
|
+
...userFromProfile,
|
|
318
|
+
// The user's id is intentionally not set based on the profile id, as
|
|
319
|
+
// the user should remain independent of the provider and the profile id
|
|
320
|
+
// is saved on the Account already, as `providerAccountId`.
|
|
321
|
+
id: crypto.randomUUID(),
|
|
322
|
+
email: userFromProfile.email?.toLowerCase(),
|
|
323
|
+
} satisfies User
|
|
324
|
+
|
|
325
|
+
return {
|
|
326
|
+
user,
|
|
327
|
+
account: {
|
|
328
|
+
...tokens,
|
|
329
|
+
provider: provider.id,
|
|
330
|
+
type: provider.type,
|
|
331
|
+
providerAccountId: userFromProfile.id ?? crypto.randomUUID(),
|
|
332
|
+
},
|
|
333
|
+
}
|
|
334
|
+
} catch (e) {
|
|
335
|
+
// If we didn't get a response either there was a problem with the provider
|
|
336
|
+
// response *or* the user cancelled the action with the provider.
|
|
337
|
+
//
|
|
338
|
+
// Unfortunately, we can't tell which - at least not in a way that works for
|
|
339
|
+
// all providers, so we return an empty object; the user should then be
|
|
340
|
+
// redirected back to the sign up page. We log the error to help developers
|
|
341
|
+
// who might be trying to debug this when configuring a new provider.
|
|
342
|
+
logger.debug("getProfile error details", OAuthProfile)
|
|
343
|
+
logger.error(
|
|
344
|
+
new OAuthProfileParseError(e as Error, { provider: provider.id })
|
|
345
|
+
)
|
|
346
|
+
}
|
|
347
|
+
}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import * as o from "oauth4webapi"
|
|
2
|
+
import { InvalidCheck } from "../../../../errors.js"
|
|
3
|
+
|
|
4
|
+
// NOTE: We use the default JWT methods here because they encrypt/decrypt the payload, not just sign it.
|
|
5
|
+
import { decode, encode } from "../../../../jwt.js"
|
|
6
|
+
|
|
7
|
+
import type {
|
|
8
|
+
CookiesOptions,
|
|
9
|
+
InternalOptions,
|
|
10
|
+
RequestInternal,
|
|
11
|
+
User,
|
|
12
|
+
} from "../../../../types.js"
|
|
13
|
+
import type { Cookie } from "../../../utils/cookie.js"
|
|
14
|
+
import type { WebAuthnProviderType } from "../../../../providers/webauthn.js"
|
|
15
|
+
|
|
16
|
+
interface CookiePayload {
|
|
17
|
+
value: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const COOKIE_TTL = 60 * 15 // 15 minutes
|
|
21
|
+
|
|
22
|
+
/** Returns a cookie with a JWT encrypted payload. */
|
|
23
|
+
async function sealCookie(
|
|
24
|
+
name: keyof CookiesOptions,
|
|
25
|
+
payload: string,
|
|
26
|
+
options: InternalOptions<"oauth" | "oidc" | WebAuthnProviderType>
|
|
27
|
+
): Promise<Cookie> {
|
|
28
|
+
const { cookies, logger } = options
|
|
29
|
+
const cookie = cookies[name]
|
|
30
|
+
const expires = new Date()
|
|
31
|
+
expires.setTime(expires.getTime() + COOKIE_TTL * 1000)
|
|
32
|
+
|
|
33
|
+
logger.debug(`CREATE_${name.toUpperCase()}`, {
|
|
34
|
+
name: cookie.name,
|
|
35
|
+
payload,
|
|
36
|
+
COOKIE_TTL,
|
|
37
|
+
expires,
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
const encoded = await encode({
|
|
41
|
+
...options.jwt,
|
|
42
|
+
maxAge: COOKIE_TTL,
|
|
43
|
+
token: { value: payload } satisfies CookiePayload,
|
|
44
|
+
salt: cookie.name,
|
|
45
|
+
})
|
|
46
|
+
const cookieOptions = { ...cookie.options, expires }
|
|
47
|
+
return { name: cookie.name, value: encoded, options: cookieOptions }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function parseCookie(
|
|
51
|
+
name: keyof CookiesOptions,
|
|
52
|
+
value: string | undefined,
|
|
53
|
+
options: InternalOptions
|
|
54
|
+
): Promise<string> {
|
|
55
|
+
try {
|
|
56
|
+
const { logger, cookies, jwt } = options
|
|
57
|
+
logger.debug(`PARSE_${name.toUpperCase()}`, { cookie: value })
|
|
58
|
+
|
|
59
|
+
if (!value) throw new InvalidCheck(`${name} cookie was missing`)
|
|
60
|
+
const parsed = await decode<CookiePayload>({
|
|
61
|
+
...jwt,
|
|
62
|
+
token: value,
|
|
63
|
+
salt: cookies[name].name,
|
|
64
|
+
})
|
|
65
|
+
if (parsed?.value) return parsed.value
|
|
66
|
+
throw new Error("Invalid cookie")
|
|
67
|
+
} catch (error) {
|
|
68
|
+
throw new InvalidCheck(`${name} value could not be parsed`, {
|
|
69
|
+
cause: error,
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function clearCookie(
|
|
75
|
+
name: keyof CookiesOptions,
|
|
76
|
+
options: InternalOptions,
|
|
77
|
+
resCookies: Cookie[]
|
|
78
|
+
) {
|
|
79
|
+
const { logger, cookies } = options
|
|
80
|
+
const cookie = cookies[name]
|
|
81
|
+
logger.debug(`CLEAR_${name.toUpperCase()}`, { cookie })
|
|
82
|
+
resCookies.push({
|
|
83
|
+
name: cookie.name,
|
|
84
|
+
value: "",
|
|
85
|
+
options: { ...cookies[name].options, maxAge: 0 },
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function useCookie(
|
|
90
|
+
check: "state" | "pkce" | "nonce",
|
|
91
|
+
name: keyof CookiesOptions
|
|
92
|
+
) {
|
|
93
|
+
return async function (
|
|
94
|
+
cookies: RequestInternal["cookies"],
|
|
95
|
+
resCookies: Cookie[],
|
|
96
|
+
options: InternalOptions<"oidc">
|
|
97
|
+
) {
|
|
98
|
+
const { provider, logger } = options
|
|
99
|
+
if (!provider?.checks?.includes(check)) return
|
|
100
|
+
const cookieValue = cookies?.[options.cookies[name].name]
|
|
101
|
+
logger.debug(`USE_${name.toUpperCase()}`, { value: cookieValue })
|
|
102
|
+
const parsed = await parseCookie(name, cookieValue, options)
|
|
103
|
+
clearCookie(name, options, resCookies)
|
|
104
|
+
return parsed
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* @see https://www.rfc-editor.org/rfc/rfc7636
|
|
110
|
+
* @see https://danielfett.de/2020/05/16/pkce-vs-nonce-equivalent-or-not/#pkce
|
|
111
|
+
*/
|
|
112
|
+
export const pkce = {
|
|
113
|
+
/** Creates a PKCE code challenge and verifier pair. The verifier in stored in the cookie. */
|
|
114
|
+
async create(options: InternalOptions<"oauth">) {
|
|
115
|
+
const code_verifier = o.generateRandomCodeVerifier()
|
|
116
|
+
const value = await o.calculatePKCECodeChallenge(code_verifier)
|
|
117
|
+
const cookie = await sealCookie("pkceCodeVerifier", code_verifier, options)
|
|
118
|
+
return { cookie, value }
|
|
119
|
+
},
|
|
120
|
+
/**
|
|
121
|
+
* Returns code_verifier if the provider is configured to use PKCE,
|
|
122
|
+
* and clears the container cookie afterwards.
|
|
123
|
+
* An error is thrown if the code_verifier is missing or invalid.
|
|
124
|
+
*/
|
|
125
|
+
use: useCookie("pkce", "pkceCodeVerifier"),
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
interface EncodedState {
|
|
129
|
+
origin?: string
|
|
130
|
+
random: string
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const STATE_MAX_AGE = 60 * 15 // 15 minutes in seconds
|
|
134
|
+
const encodedStateSalt = "encodedState"
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* @see https://www.rfc-editor.org/rfc/rfc6749#section-10.12
|
|
138
|
+
* @see https://www.rfc-editor.org/rfc/rfc6749#section-4.1.1
|
|
139
|
+
*/
|
|
140
|
+
export const state = {
|
|
141
|
+
/** Creates a state cookie with an optionally encoded body. */
|
|
142
|
+
async create(options: InternalOptions<"oauth">, origin?: string) {
|
|
143
|
+
const { provider } = options
|
|
144
|
+
if (!provider.checks.includes("state")) {
|
|
145
|
+
if (origin) {
|
|
146
|
+
throw new InvalidCheck(
|
|
147
|
+
"State data was provided but the provider is not configured to use state"
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
return
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// IDEA: Allow the user to pass data to be stored in the state
|
|
154
|
+
const payload = {
|
|
155
|
+
origin,
|
|
156
|
+
random: o.generateRandomState(),
|
|
157
|
+
} satisfies EncodedState
|
|
158
|
+
const value = await encode({
|
|
159
|
+
secret: options.jwt.secret,
|
|
160
|
+
token: payload,
|
|
161
|
+
salt: encodedStateSalt,
|
|
162
|
+
maxAge: STATE_MAX_AGE,
|
|
163
|
+
})
|
|
164
|
+
const cookie = await sealCookie("state", value, options)
|
|
165
|
+
|
|
166
|
+
return { cookie, value }
|
|
167
|
+
},
|
|
168
|
+
/**
|
|
169
|
+
* Returns state if the provider is configured to use state,
|
|
170
|
+
* and clears the container cookie afterwards.
|
|
171
|
+
* An error is thrown if the state is missing or invalid.
|
|
172
|
+
*/
|
|
173
|
+
use: useCookie("state", "state"),
|
|
174
|
+
/** Decodes the state. If it could not be decoded, it throws an error. */
|
|
175
|
+
async decode(state: string, options: InternalOptions) {
|
|
176
|
+
try {
|
|
177
|
+
options.logger.debug("DECODE_STATE", { state })
|
|
178
|
+
const payload = await decode<EncodedState>({
|
|
179
|
+
secret: options.jwt.secret,
|
|
180
|
+
token: state,
|
|
181
|
+
salt: encodedStateSalt,
|
|
182
|
+
})
|
|
183
|
+
if (payload) return payload
|
|
184
|
+
throw new Error("Invalid state")
|
|
185
|
+
} catch (error) {
|
|
186
|
+
throw new InvalidCheck("State could not be decoded", { cause: error })
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export const nonce = {
|
|
192
|
+
async create(options: InternalOptions<"oidc">) {
|
|
193
|
+
if (!options.provider.checks.includes("nonce")) return
|
|
194
|
+
const value = o.generateRandomNonce()
|
|
195
|
+
const cookie = await sealCookie("nonce", value, options)
|
|
196
|
+
return { cookie, value }
|
|
197
|
+
},
|
|
198
|
+
/**
|
|
199
|
+
* Returns nonce if the provider is configured to use nonce,
|
|
200
|
+
* and clears the container cookie afterwards.
|
|
201
|
+
* An error is thrown if the nonce is missing or invalid.
|
|
202
|
+
* @see https://openid.net/specs/openid-connect-core-1_0.html#NonceNotes
|
|
203
|
+
* @see https://danielfett.de/2020/05/16/pkce-vs-nonce-equivalent-or-not/#nonce
|
|
204
|
+
*/
|
|
205
|
+
use: useCookie("nonce", "nonce"),
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const WEBAUTHN_CHALLENGE_MAX_AGE = 60 * 15 // 15 minutes in seconds
|
|
209
|
+
|
|
210
|
+
interface WebAuthnChallengePayload {
|
|
211
|
+
challenge: string
|
|
212
|
+
registerData?: User
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const webauthnChallengeSalt = "encodedWebauthnChallenge"
|
|
216
|
+
export const webauthnChallenge = {
|
|
217
|
+
async create(
|
|
218
|
+
options: InternalOptions<WebAuthnProviderType>,
|
|
219
|
+
challenge: string,
|
|
220
|
+
registerData?: User
|
|
221
|
+
) {
|
|
222
|
+
return {
|
|
223
|
+
cookie: await sealCookie(
|
|
224
|
+
"webauthnChallenge",
|
|
225
|
+
await encode({
|
|
226
|
+
secret: options.jwt.secret,
|
|
227
|
+
token: { challenge, registerData } satisfies WebAuthnChallengePayload,
|
|
228
|
+
salt: webauthnChallengeSalt,
|
|
229
|
+
maxAge: WEBAUTHN_CHALLENGE_MAX_AGE,
|
|
230
|
+
}),
|
|
231
|
+
options
|
|
232
|
+
),
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
/** Returns WebAuthn challenge if present. */
|
|
236
|
+
async use(
|
|
237
|
+
options: InternalOptions<WebAuthnProviderType>,
|
|
238
|
+
cookies: RequestInternal["cookies"],
|
|
239
|
+
resCookies: Cookie[]
|
|
240
|
+
): Promise<WebAuthnChallengePayload> {
|
|
241
|
+
const cookieValue = cookies?.[options.cookies.webauthnChallenge.name]
|
|
242
|
+
|
|
243
|
+
const parsed = await parseCookie("webauthnChallenge", cookieValue, options)
|
|
244
|
+
|
|
245
|
+
const payload = await decode<WebAuthnChallengePayload>({
|
|
246
|
+
secret: options.jwt.secret,
|
|
247
|
+
token: parsed,
|
|
248
|
+
salt: webauthnChallengeSalt,
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
// Clear the WebAuthn challenge cookie after use
|
|
252
|
+
clearCookie("webauthnChallenge", options, resCookies)
|
|
253
|
+
|
|
254
|
+
if (!payload) throw new InvalidCheck("WebAuthn challenge was missing")
|
|
255
|
+
|
|
256
|
+
return payload
|
|
257
|
+
},
|
|
258
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { createHash, randomString } from "../../../utils/web.js"
|
|
2
|
+
|
|
3
|
+
import type { AuthAction, InternalOptions } from "../../../../types.js"
|
|
4
|
+
import { MissingCSRF } from "../../../../errors.js"
|
|
5
|
+
interface CreateCSRFTokenParams {
|
|
6
|
+
options: InternalOptions
|
|
7
|
+
cookieValue?: string
|
|
8
|
+
isPost: boolean
|
|
9
|
+
bodyValue?: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Ensure CSRF Token cookie is set for any subsequent requests.
|
|
14
|
+
* Used as part of the strategy for mitigation for CSRF tokens.
|
|
15
|
+
*
|
|
16
|
+
* Creates a cookie like 'next-auth.csrf-token' with the value 'token|hash',
|
|
17
|
+
* where 'token' is the CSRF token and 'hash' is a hash made of the token and
|
|
18
|
+
* the secret, and the two values are joined by a pipe '|'. By storing the
|
|
19
|
+
* value and the hash of the value (with the secret used as a salt) we can
|
|
20
|
+
* verify the cookie was set by the server and not by a malicious attacker.
|
|
21
|
+
*
|
|
22
|
+
* For more details, see the following OWASP links:
|
|
23
|
+
* https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#double-submit-cookie
|
|
24
|
+
* https://owasp.org/www-chapter-london/assets/slides/David_Johansson-Double_Defeat_of_Double-Submit_Cookie.pdf
|
|
25
|
+
*/
|
|
26
|
+
export async function createCSRFToken({
|
|
27
|
+
options,
|
|
28
|
+
cookieValue,
|
|
29
|
+
isPost,
|
|
30
|
+
bodyValue,
|
|
31
|
+
}: CreateCSRFTokenParams) {
|
|
32
|
+
if (cookieValue) {
|
|
33
|
+
const [csrfToken, csrfTokenHash] = cookieValue.split("|")
|
|
34
|
+
|
|
35
|
+
const expectedCsrfTokenHash = await createHash(
|
|
36
|
+
`${csrfToken}${options.secret}`
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
if (csrfTokenHash === expectedCsrfTokenHash) {
|
|
40
|
+
// If hash matches then we trust the CSRF token value
|
|
41
|
+
// If this is a POST request and the CSRF Token in the POST request matches
|
|
42
|
+
// the cookie we have already verified is the one we have set, then the token is verified!
|
|
43
|
+
const csrfTokenVerified = isPost && csrfToken === bodyValue
|
|
44
|
+
|
|
45
|
+
return { csrfTokenVerified, csrfToken }
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// New CSRF token
|
|
50
|
+
const csrfToken = randomString(32)
|
|
51
|
+
const csrfTokenHash = await createHash(`${csrfToken}${options.secret}`)
|
|
52
|
+
const cookie = `${csrfToken}|${csrfTokenHash}`
|
|
53
|
+
|
|
54
|
+
return { cookie, csrfToken }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function validateCSRF(action: AuthAction, verified?: boolean) {
|
|
58
|
+
if (verified) return
|
|
59
|
+
throw new MissingCSRF(`CSRF token was missing during an action ${action}`)
|
|
60
|
+
}
|