From 93be112a9454c894c1ce3146ed377e6a6aeca64a Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Sun, 17 Dec 2023 13:54:55 +0200 Subject: [PATCH] Azure AD B2C login using OAuth2. https://github.com/wekan/wekan/wiki/B2C Thanks to snowsky and xet7 ! Fixes #5242 --- .devcontainer/Dockerfile | 1 + Dockerfile | 1 + docker-compose.yml | 2 ++ packages/wekan-oidc/oidc_server.js | 16 ++++++++++++++-- releases/virtualbox/start-wekan.sh | 2 ++ sandstorm-pkgdef.capnp | 1 + snap-src/bin/config | 6 +++++- snap-src/bin/wekan-help | 6 ++++++ start-wekan.bat | 3 +++ start-wekan.sh | 3 +++ torodb-postgresql/docker-compose.yml | 2 ++ 11 files changed, 40 insertions(+), 3 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 5ba76383d..0063e5ec3 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -54,6 +54,7 @@ ENV \ OAUTH2_ENABLED=false \ OAUTH2_CA_CERT="" \ OAUTH2_ADFS_ENABLED=false \ + OAUTH2_B2C_ENABLED=false \ OAUTH2_LOGIN_STYLE=redirect \ OAUTH2_CLIENT_ID="" \ OAUTH2_SECRET="" \ diff --git a/Dockerfile b/Dockerfile index 90c8426d6..434978ba0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -69,6 +69,7 @@ ENV BUILD_DEPS="apt-utils libarchive-tools gnupg gosu wget curl bzip2 g++ build- OIDC_REDIRECTION_ENABLED=false \ OAUTH2_CA_CERT="" \ OAUTH2_ADFS_ENABLED=false \ + OAUTH2_B2C_ENABLED=false \ OAUTH2_LOGIN_STYLE=redirect \ OAUTH2_CLIENT_ID="" \ OAUTH2_SECRET="" \ diff --git a/docker-compose.yml b/docker-compose.yml index 69738e6c1..1821b57d1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -391,6 +391,8 @@ services: #- OAUTH2_CA_CERT=ABCD1234 # Use OAuth2 ADFS additional changes. Also needs OAUTH2_ENABLED=true setting. #- OAUTH2_ADFS_ENABLED=false + # Azure AD B2C. https://github.com/wekan/wekan/issues/5242 + #- OAUTH2_B2C_ENABLED=false # OAuth2 login style: popup or redirect. #- OAUTH2_LOGIN_STYLE=redirect # Application GUID captured during app registration: diff --git a/packages/wekan-oidc/oidc_server.js b/packages/wekan-oidc/oidc_server.js index 35b7f922c..93f267c18 100644 --- a/packages/wekan-oidc/oidc_server.js +++ b/packages/wekan-oidc/oidc_server.js @@ -27,11 +27,14 @@ OAuth.registerService('oidc', 2, null, function (query) { var accessToken = token.access_token || token.id_token; var expiresAt = (+new Date) + (1000 * parseInt(token.expires_in, 10)); - var claimsInAccessToken = (process.env.OAUTH2_ADFS_ENABLED === 'true' || process.env.OAUTH2_ADFS_ENABLED === true) || false; + var claimsInAccessToken = (process.env.OAUTH2_ADFS_ENABLED === 'true' || + process.env.OAUTH2_ADFS_ENABLED === true || + process.env.OAUTH2_B2C_ENABLED === 'true' || + process.env.OAUTH2_B2C_ENABLED === true) || false; if(claimsInAccessToken) { - // hack when using custom claims in the accessToken. On premise ADFS + // hack when using custom claims in the accessToken. On premise ADFS. And Azure AD B2C. userinfo = getTokenContent(accessToken); } else @@ -64,6 +67,10 @@ OAuth.registerService('oidc', 2, null, function (query) { serviceData.email = userinfo[process.env.OAUTH2_EMAIL_MAP]; // || userinfo["email"]; } + if (process.env.OAUTH2_B2C_ENABLED === 'true' || process.env.OAUTH2_B2C_ENABLED === true) { + serviceData.email = userinfo["emails"][0]; + } + if (accessToken) { var tokenContent = getTokenContent(accessToken); var fields = _.pick(tokenContent, getConfiguration().idTokenWhitelistFields); @@ -76,6 +83,11 @@ OAuth.registerService('oidc', 2, null, function (query) { profile.name = userinfo[process.env.OAUTH2_FULLNAME_MAP]; // || userinfo["displayName"]; profile.email = userinfo[process.env.OAUTH2_EMAIL_MAP]; // || userinfo["email"]; + + if (process.env.OAUTH2_B2C_ENABLED === 'true' || process.env.OAUTH2_B2C_ENABLED === true) { + profile.email = userinfo["emails"][0]; + } + if (debug) console.log('XXX: profile:', profile); diff --git a/releases/virtualbox/start-wekan.sh b/releases/virtualbox/start-wekan.sh index 1763b3c1b..09ec12765 100755 --- a/releases/virtualbox/start-wekan.sh +++ b/releases/virtualbox/start-wekan.sh @@ -154,6 +154,8 @@ #export OAUTH2_ENABLED=true # Use OAuth2 ADFS additional changes. Also needs OAUTH2_ENABLED=true setting. #export OAUTH2_ADFS_ENABLED=false + # Azure AD B2C. https://github.com/wekan/wekan/issues/5242 + #- OAUTH2_B2C_ENABLED=false # OAuth2 docs: https://github.com/wekan/wekan/wiki/OAuth2 # OAuth2 login style: popup or redirect. #export OAUTH2_LOGIN_STYLE=redirect diff --git a/sandstorm-pkgdef.capnp b/sandstorm-pkgdef.capnp index a53c6260e..9a2b4e8f1 100644 --- a/sandstorm-pkgdef.capnp +++ b/sandstorm-pkgdef.capnp @@ -258,6 +258,7 @@ const myCommand :Spk.Manifest.Command = ( (key = "OAUTH2_ENABLED", value="false"), (key = "OAUTH2_CA_CERT", value=""), (key = "OAUTH2_ADFS_ENABLED", value="false"), + (key = "OAUTH2_B2C_ENABLED", value="false"), (key = "OAUTH2_CLIENT_ID", value="false"), (key = "OAUTH2_SECRET", value=""), (key = "OAUTH2_SERVER_URL", value=""), diff --git a/snap-src/bin/config b/snap-src/bin/config index 77e71afca..3265f3aa0 100755 --- a/snap-src/bin/config +++ b/snap-src/bin/config @@ -3,7 +3,7 @@ # All supported keys are defined here together with descriptions and default values # list of supported keys -keys="DEBUG S3 MONGO_LOG_DESTINATION MONGO_URL MONGODB_BIND_UNIX_SOCKET MONGO_URL MONGODB_BIND_IP MONGODB_PORT MAIL_URL MAIL_FROM MAIL_SERVICE MAIL_SERVICE_USER MAIL_SERVICE_PASSWORD ROOT_URL PORT DISABLE_MONGODB CADDY_ENABLED CADDY_BIND_PORT WITH_API RICHER_CARD_COMMENT_EDITOR CARD_OPENED_WEBHOOK_ENABLED ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURES_BEFORE ACCOUNTS_LOCKOUT_KNOWN_USERS_PERIOD ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURE_WINDOW ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURES_BERORE ACCOUNTS_LOCKOUT_UNKNOWN_USERS_LOCKOUT_PERIOD ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURE_WINDOW ACCOUNTS_COMMON_LOGIN_EXPIRATION_IN_DAYS ATTACHMENTS_UPLOAD_EXTERNAL_PROGRAM ATTACHMENTS_UPLOAD_MIME_TYPES ATTACHMENTS_UPLOAD_MAX_SIZE AVATARS_UPLOAD_EXTERNAL_PROGRAM AVATARS_UPLOAD_MIME_TYPES AVATARS_UPLOAD_MAX_SIZE MAX_IMAGE_PIXEL IMAGE_COMPRESS_RATIO BIGEVENTS_PATTERN NOTIFICATION_TRAY_AFTER_READ_DAYS_BEFORE_REMOVE NOTIFY_DUE_DAYS_BEFORE_AND_AFTER NOTIFY_DUE_AT_HOUR_OF_DAY DEFAULT_BOARD_ID EMAIL_NOTIFICATION_TIMEOUT CORS CORS_ALLOW_HEADERS CORS_EXPOSE_HEADERS MATOMO_ADDRESS MATOMO_SITE_ID MATOMO_DO_NOT_TRACK MATOMO_WITH_USERNAME METRICS_ALLOWED_IP_ADDRESSES BROWSER_POLICY_ENABLED TRUSTED_URL WEBHOOKS_ATTRIBUTES OAUTH2_ENABLED OIDC_REDIRECTION_ENABLED OAUTH2_CA_CERT OAUTH2_LOGIN_STYLE OAUTH2_CLIENT_ID OAUTH2_SECRET OAUTH2_SERVER_URL OAUTH2_AUTH_ENDPOINT OAUTH2_USERINFO_ENDPOINT OAUTH2_TOKEN_ENDPOINT OAUTH2_ID_MAP OAUTH2_USERNAME_MAP OAUTH2_FULLNAME_MAP OAUTH2_ID_TOKEN_WHITELIST_FIELDS OAUTH2_EMAIL_MAP OAUTH2_REQUEST_PERMISSIONS OAUTH2_ADFS_ENABLED LDAP_ENABLE LDAP_PORT LDAP_HOST LDAP_AD_SIMPLE_AUTH LDAP_BASEDN LDAP_LOGIN_FALLBACK LDAP_RECONNECT LDAP_TIMEOUT LDAP_IDLE_TIMEOUT LDAP_CONNECT_TIMEOUT LDAP_AUTHENTIFICATION LDAP_AUTHENTIFICATION_USERDN LDAP_AUTHENTIFICATION_PASSWORD LDAP_LOG_ENABLED LDAP_BACKGROUND_SYNC LDAP_BACKGROUND_SYNC_INTERVAL LDAP_BACKGROUND_SYNC_KEEP_EXISTANT_USERS_UPDATED LDAP_BACKGROUND_SYNC_IMPORT_NEW_USERS LDAP_ENCRYPTION LDAP_CA_CERT LDAP_REJECT_UNAUTHORIZED LDAP_USER_AUTHENTICATION LDAP_USER_AUTHENTICATION_FIELD LDAP_USER_SEARCH_FILTER LDAP_USER_SEARCH_SCOPE LDAP_USER_SEARCH_FIELD LDAP_SEARCH_PAGE_SIZE LDAP_SEARCH_SIZE_LIMIT LDAP_GROUP_FILTER_ENABLE LDAP_GROUP_FILTER_OBJECTCLASS LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT LDAP_GROUP_FILTER_GROUP_NAME LDAP_UNIQUE_IDENTIFIER_FIELD LDAP_UTF8_NAMES_SLUGIFY LDAP_USERNAME_FIELD LDAP_FULLNAME_FIELD LDAP_MERGE_EXISTING_USERS LDAP_SYNC_USER_DATA LDAP_SYNC_USER_DATA_FIELDMAP LDAP_SYNC_GROUP_ROLES LDAP_DEFAULT_DOMAIN LDAP_EMAIL_MATCH_ENABLE LDAP_EMAIL_MATCH_REQUIRE LDAP_EMAIL_MATCH_VERIFIED LDAP_EMAIL_FIELD LDAP_SYNC_ADMIN_STATUS LDAP_SYNC_ADMIN_GROUPS HEADER_LOGIN_ID HEADER_LOGIN_FIRSTNAME HEADER_LOGIN_LASTNAME HEADER_LOGIN_EMAIL LOGOUT_WITH_TIMER LOGOUT_IN LOGOUT_ON_HOURS LOGOUT_ON_MINUTES DEFAULT_AUTHENTICATION_METHOD PASSWORD_LOGIN_ENABLED CAS_ENABLED CAS_BASE_URL CAS_LOGIN_URL CAS_VALIDATE_URL SAML_ENABLED SAML_PROVIDER SAML_ENTRYPOINT SAML_ISSUER SAML_CERT SAML_IDPSLO_REDIRECTURL SAML_PRIVATE_KEYFILE SAML_PUBLIC_CERTFILE SAML_IDENTIFIER_FORMAT SAML_LOCAL_PROFILE_MATCH_ATTRIBUTE SAML_ATTRIBUTES ORACLE_OIM_ENABLED RESULTS_PER_PAGE WAIT_SPINNER NODE_OPTIONS" +keys="DEBUG S3 MONGO_LOG_DESTINATION MONGO_URL MONGODB_BIND_UNIX_SOCKET MONGO_URL MONGODB_BIND_IP MONGODB_PORT MAIL_URL MAIL_FROM MAIL_SERVICE MAIL_SERVICE_USER MAIL_SERVICE_PASSWORD ROOT_URL PORT DISABLE_MONGODB CADDY_ENABLED CADDY_BIND_PORT WITH_API RICHER_CARD_COMMENT_EDITOR CARD_OPENED_WEBHOOK_ENABLED ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURES_BEFORE ACCOUNTS_LOCKOUT_KNOWN_USERS_PERIOD ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURE_WINDOW ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURES_BERORE ACCOUNTS_LOCKOUT_UNKNOWN_USERS_LOCKOUT_PERIOD ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURE_WINDOW ACCOUNTS_COMMON_LOGIN_EXPIRATION_IN_DAYS ATTACHMENTS_UPLOAD_EXTERNAL_PROGRAM ATTACHMENTS_UPLOAD_MIME_TYPES ATTACHMENTS_UPLOAD_MAX_SIZE AVATARS_UPLOAD_EXTERNAL_PROGRAM AVATARS_UPLOAD_MIME_TYPES AVATARS_UPLOAD_MAX_SIZE MAX_IMAGE_PIXEL IMAGE_COMPRESS_RATIO BIGEVENTS_PATTERN NOTIFICATION_TRAY_AFTER_READ_DAYS_BEFORE_REMOVE NOTIFY_DUE_DAYS_BEFORE_AND_AFTER NOTIFY_DUE_AT_HOUR_OF_DAY DEFAULT_BOARD_ID EMAIL_NOTIFICATION_TIMEOUT CORS CORS_ALLOW_HEADERS CORS_EXPOSE_HEADERS MATOMO_ADDRESS MATOMO_SITE_ID MATOMO_DO_NOT_TRACK MATOMO_WITH_USERNAME METRICS_ALLOWED_IP_ADDRESSES BROWSER_POLICY_ENABLED TRUSTED_URL WEBHOOKS_ATTRIBUTES OAUTH2_ENABLED OIDC_REDIRECTION_ENABLED OAUTH2_CA_CERT OAUTH2_LOGIN_STYLE OAUTH2_CLIENT_ID OAUTH2_SECRET OAUTH2_SERVER_URL OAUTH2_AUTH_ENDPOINT OAUTH2_USERINFO_ENDPOINT OAUTH2_TOKEN_ENDPOINT OAUTH2_ID_MAP OAUTH2_USERNAME_MAP OAUTH2_FULLNAME_MAP OAUTH2_ID_TOKEN_WHITELIST_FIELDS OAUTH2_EMAIL_MAP OAUTH2_REQUEST_PERMISSIONS OAUTH2_ADFS_ENABLED OAUTH2_B2C_ENABLED LDAP_ENABLE LDAP_PORT LDAP_HOST LDAP_AD_SIMPLE_AUTH LDAP_BASEDN LDAP_LOGIN_FALLBACK LDAP_RECONNECT LDAP_TIMEOUT LDAP_IDLE_TIMEOUT LDAP_CONNECT_TIMEOUT LDAP_AUTHENTIFICATION LDAP_AUTHENTIFICATION_USERDN LDAP_AUTHENTIFICATION_PASSWORD LDAP_LOG_ENABLED LDAP_BACKGROUND_SYNC LDAP_BACKGROUND_SYNC_INTERVAL LDAP_BACKGROUND_SYNC_KEEP_EXISTANT_USERS_UPDATED LDAP_BACKGROUND_SYNC_IMPORT_NEW_USERS LDAP_ENCRYPTION LDAP_CA_CERT LDAP_REJECT_UNAUTHORIZED LDAP_USER_AUTHENTICATION LDAP_USER_AUTHENTICATION_FIELD LDAP_USER_SEARCH_FILTER LDAP_USER_SEARCH_SCOPE LDAP_USER_SEARCH_FIELD LDAP_SEARCH_PAGE_SIZE LDAP_SEARCH_SIZE_LIMIT LDAP_GROUP_FILTER_ENABLE LDAP_GROUP_FILTER_OBJECTCLASS LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT LDAP_GROUP_FILTER_GROUP_NAME LDAP_UNIQUE_IDENTIFIER_FIELD LDAP_UTF8_NAMES_SLUGIFY LDAP_USERNAME_FIELD LDAP_FULLNAME_FIELD LDAP_MERGE_EXISTING_USERS LDAP_SYNC_USER_DATA LDAP_SYNC_USER_DATA_FIELDMAP LDAP_SYNC_GROUP_ROLES LDAP_DEFAULT_DOMAIN LDAP_EMAIL_MATCH_ENABLE LDAP_EMAIL_MATCH_REQUIRE LDAP_EMAIL_MATCH_VERIFIED LDAP_EMAIL_FIELD LDAP_SYNC_ADMIN_STATUS LDAP_SYNC_ADMIN_GROUPS HEADER_LOGIN_ID HEADER_LOGIN_FIRSTNAME HEADER_LOGIN_LASTNAME HEADER_LOGIN_EMAIL LOGOUT_WITH_TIMER LOGOUT_IN LOGOUT_ON_HOURS LOGOUT_ON_MINUTES DEFAULT_AUTHENTICATION_METHOD PASSWORD_LOGIN_ENABLED CAS_ENABLED CAS_BASE_URL CAS_LOGIN_URL CAS_VALIDATE_URL SAML_ENABLED SAML_PROVIDER SAML_ENTRYPOINT SAML_ISSUER SAML_CERT SAML_IDPSLO_REDIRECTURL SAML_PRIVATE_KEYFILE SAML_PUBLIC_CERTFILE SAML_IDENTIFIER_FORMAT SAML_LOCAL_PROFILE_MATCH_ATTRIBUTE SAML_ATTRIBUTES ORACLE_OIM_ENABLED RESULTS_PER_PAGE WAIT_SPINNER NODE_OPTIONS" DESCRIPTION_S3='AWS S3 for files. Example: {"s3":{"key": "xxx", "secret": "xxx", "bucket": "xxx", "region": "eu-west-1"}}' DEFAULT_S3="" @@ -267,6 +267,10 @@ DESCRIPTION_OAUTH2_ADFS_ENABLED="Enable OAuth2 ADFS. Default: false" DEFAULT_OAUTH2_ADFS_ENABLED="false" KEY_OAUTH2_ADFS_ENABLED="oauth2-adfs-enabled" +DESCRIPTION_OAUTH2_B2C_ENABLED="Enable OAuth2 Azure AD B2C https://github.com/wekan/wekan/issues/5242. Default: false" +DEFAULT_OAUTH2_B2C_ENABLED="false" +KEY_OAUTH2_B2C_ENABLED="oauth2-b2c-enabled" + DESCRIPTION_OAUTH2_LOGIN_STYLE="OAuth2 login style: popup or redirect. Default: redirect" DEFAULT_OAUTH2_LOGIN_STYLE="redirect" KEY_OAUTH2_LOGIN_STYLE="oauth2-login-style" diff --git a/snap-src/bin/wekan-help b/snap-src/bin/wekan-help index 4a74d15e5..32365cf28 100755 --- a/snap-src/bin/wekan-help +++ b/snap-src/bin/wekan-help @@ -310,6 +310,12 @@ echo -e "\t$ snap set $SNAP_NAME oauth2-adfs-enabled='true'" echo -e "\t-Disable the OAuth2 ADFS of Wekan:" echo -e "\t$ snap unset $SNAP_NAME oauth2-adfs-enabled" echo -e "\n" +echo -e "OAuth2 Azure AD B2C Enabled. Also requires oauth2-enabled='true' . https://github.com/wekan/wekan/issues/5242." +echo -e "To enable the OAuth2 Azure AD B2C of Wekan:" +echo -e "\t$ snap set $SNAP_NAME oauth2-b2c-enabled='true'" +echo -e "\t-Disable the OAuth2 Azure AD B2C of Wekan:" +echo -e "\t$ snap unset $SNAP_NAME oauth2-b2c-enabled" +echo -e "\n" echo -e "OAuth2 Client ID." echo -e "To enable the OAuth2 Client ID of Wekan:" echo -e "\t$ snap set $SNAP_NAME oauth2-client-id='54321abcde'" diff --git a/start-wekan.bat b/start-wekan.bat index 08bee2a20..50940d2ee 100644 --- a/start-wekan.bat +++ b/start-wekan.bat @@ -194,6 +194,9 @@ REM SET OAUTH2_CA_CERT=ABCD1234 REM # Use OAuth2 ADFS additional changes. Also needs OAUTH2_ENABLED=true setting. REM SET OAUTH2_ADFS_ENABLED=false +REM # Use OAuth2 Azure AD B2C. Also requires OAUTH2_ENABLED=true setting . https://github.com/wekan/wekan/issues/5242 +REM SET DEFAULT_OAUTH2_B2C_ENABLED=false + REM # OAuth2 Client ID, for example from Rocket.Chat. Example: abcde12345 REM # example: OAUTH2_CLIENT_ID=abcde12345 REM SET OAUTH2_CLIENT_ID= diff --git a/start-wekan.sh b/start-wekan.sh index a936e3a4f..c51c8ff45 100755 --- a/start-wekan.sh +++ b/start-wekan.sh @@ -195,6 +195,9 @@ # Use OAuth2 ADFS additional changes. Also needs OAUTH2_ENABLED=true setting. #export OAUTH2_ADFS_ENABLED=false # + # Azure AD B2C. https://github.com/wekan/wekan/issues/5242 + #export OAUTH2_B2C_ENABLED=false + # # OAuth2 docs: https://github.com/wekan/wekan/wiki/OAuth2 # OAuth2 login style: popup or redirect. #export OAUTH2_LOGIN_STYLE=redirect diff --git a/torodb-postgresql/docker-compose.yml b/torodb-postgresql/docker-compose.yml index 0307705c5..fed2910d9 100644 --- a/torodb-postgresql/docker-compose.yml +++ b/torodb-postgresql/docker-compose.yml @@ -394,6 +394,8 @@ services: #- OAUTH2_CA_CERT=ABCD1234 # Use OAuth2 ADFS additional changes. Also needs OAUTH2_ENABLED=true setting. #- OAUTH2_ADFS_ENABLED=false + # Azure AD B2C. https://github.com/wekan/wekan/issues/5242 + #- OAUTH2_B2C_ENABLED=false # OAuth2 docs: https://github.com/wekan/wekan/wiki/OAuth2 # OAuth2 Client ID, for example from Rocket.Chat. Example: abcde12345 # example: OAUTH2_CLIENT_ID=abcde12345