diff --git a/compose.yaml b/compose.yaml
index 1bf75f785..deb7fb16d 100644
--- a/compose.yaml
+++ b/compose.yaml
@@ -21,6 +21,9 @@ services:
VUE_APP_POSTHOG_API_KEY: ${VUE_APP_POSTHOG_API_KEY:-}
VUE_APP_GOOGLE_CLIENT_ID: ${CLIENT_ID:-}
VUE_APP_MICROSOFT_CLIENT_ID: ${MICROSOFT_CLIENT_ID:-}
+ VUE_APP_OIDC_PROVIDER_NAME: ${OIDC_PROVIDER_NAME:-}
+ VUE_APP_OIDC_CLIENT_ID: ${OIDC_CLIENT_ID:-}
+ VUE_APP_OIDC_AUTHORIZATION_ENDPOINT: ${OIDC_ISSUER_URL:-}/authorize
volumes:
- frontend_dist:/app/dist
command:
diff --git a/frontend/Dockerfile b/frontend/Dockerfile
index c03ed002c..9faae00bd 100644
--- a/frontend/Dockerfile
+++ b/frontend/Dockerfile
@@ -16,10 +16,16 @@ COPY . .
ARG VUE_APP_POSTHOG_API_KEY=""
ARG VUE_APP_GOOGLE_CLIENT_ID=""
ARG VUE_APP_MICROSOFT_CLIENT_ID=""
+ARG VUE_APP_OIDC_CLIENT_ID=""
+ARG VUE_APP_OIDC_AUTHORIZATION_ENDPOINT=""
+ARG VUE_APP_OIDC_PROVIDER_NAME=""
ENV VUE_APP_POSTHOG_API_KEY=$VUE_APP_POSTHOG_API_KEY
ENV VUE_APP_GOOGLE_CLIENT_ID=$VUE_APP_GOOGLE_CLIENT_ID
ENV VUE_APP_MICROSOFT_CLIENT_ID=$VUE_APP_MICROSOFT_CLIENT_ID
+ENV VUE_APP_OIDC_CLIENT_ID=$VUE_APP_OIDC_CLIENT_ID
+ENV VUE_APP_OIDC_AUTHORIZATION_ENDPOINT=$VUE_APP_OIDC_AUTHORIZATION_ENDPOINT
+ENV VUE_APP_OIDC_PROVIDER_NAME=$VUE_APP_OIDC_PROVIDER_NAME
# Build the app and move to /build so it's not hidden by volume mount
RUN npm run build && mv dist /build
diff --git a/frontend/src/App.vue b/frontend/src/App.vue
index 176178b5c..91e751372 100644
--- a/frontend/src/App.vue
+++ b/frontend/src/App.vue
@@ -236,6 +236,7 @@ import {
isPhone,
post,
signInGoogle,
+ signInOidc,
signInOutlook,
isPremiumUser,
} from "@/utils"
@@ -391,6 +392,10 @@ export default {
state,
selectAccount: true,
})
+ } else if (calendarType === calendarTypes.OIDC) {
+ signInOidc({
+ state,
+ })
}
}
},
diff --git a/frontend/src/components/SignInDialog.vue b/frontend/src/components/SignInDialog.vue
index 8077809c2..68f0084f0 100644
--- a/frontend/src/components/SignInDialog.vue
+++ b/frontend/src/components/SignInDialog.vue
@@ -45,6 +45,21 @@
+
+
+ mdi-shield-account-outline
+
+ Continue with {{ oidcProviderName }}
+
+
+
@@ -206,6 +221,17 @@ export default {
props: {
value: { type: Boolean, required: true },
},
+ computed: {
+ oidcEnabled() {
+ return !!(
+ process.env.VUE_APP_OIDC_CLIENT_ID &&
+ process.env.VUE_APP_OIDC_AUTHORIZATION_ENDPOINT
+ )
+ },
+ oidcProviderName() {
+ return process.env.VUE_APP_OIDC_PROVIDER_NAME || "SSO"
+ },
+ },
data() {
return {
calendarTypes,
diff --git a/frontend/src/constants.js b/frontend/src/constants.js
index 216d11595..52891ba9b 100644
--- a/frontend/src/constants.js
+++ b/frontend/src/constants.js
@@ -48,7 +48,8 @@ export const calendarTypes = Object.freeze({
GOOGLE: "google",
APPLE: "apple",
OUTLOOK: "outlook",
- ICS: "ics"
+ ICS: "ics",
+ OIDC: "oidc",
})
export const upgradeDialogTypes = Object.freeze({
@@ -183,4 +184,4 @@ export const guestUserId = "000000000000000000000000"
export const numFreeEvents = 3
-export const urlRegex = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/
\ No newline at end of file
+export const urlRegex = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/
diff --git a/frontend/src/utils/sign_in_utils.js b/frontend/src/utils/sign_in_utils.js
index 6f150bfe1..ce4916195 100644
--- a/frontend/src/utils/sign_in_utils.js
+++ b/frontend/src/utils/sign_in_utils.js
@@ -69,3 +69,30 @@ export const signInOutlook = ({
const url = `https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=${clientId}&response_type=code&redirect_uri=${redirectUri}&response_mode=query&scope=${scope}${stateString}`
window.location.href = url
}
+
+/**
+ * Redirects the user to the configured OIDC provider's authorization
+ * endpoint to sign in / create an account. This is authentication-only.
+ *
+ * Requires VUE_APP_OIDC_CLIENT_ID and VUE_APP_OIDC_AUTHORIZATION_ENDPOINT to
+ * be set at frontend build time.
+ */
+export const signInOidc = ({ state = {} } = {}) => {
+ const clientId = process.env.VUE_APP_OIDC_CLIENT_ID
+ const authorizationEndpoint = process.env.VUE_APP_OIDC_AUTHORIZATION_ENDPOINT
+ const redirectUri = encodeURIComponent(`${window.location.origin}/auth`)
+
+ // We put scope on `state` because not every OIDC provider echoes `scope`
+ // back on the callback. Auth.vue reads `scope ?? state.scope`.
+ const scope = "openid email profile"
+
+ if (!state) state = {}
+ state.calendarType = calendarTypes.OIDC
+ state.scope = scope
+ const stateString = `&state=${encodeURIComponent(JSON.stringify(state))}`
+
+ const url = `${authorizationEndpoint}?client_id=${clientId}&redirect_uri=${redirectUri}&response_type=code&scope=${encodeURIComponent(
+ scope,
+ )}${stateString}`
+ window.location.href = url
+}
diff --git a/frontend/src/views/SignIn.vue b/frontend/src/views/SignIn.vue
index 6366f72ca..fe11e0957 100644
--- a/frontend/src/views/SignIn.vue
+++ b/frontend/src/views/SignIn.vue
@@ -66,6 +66,22 @@
+
+
+ mdi-shield-account-outline
+
+ {{ isSignUp ? "Sign up with" : "Continue with" }}
+ {{ oidcProviderName }}
+
+
+
@@ -251,7 +267,7 @@