Configuration
Plug-Front
The only local configuration needed at Plug-Front is how to initially connect to one of the backends with failover to the next on the list. Plug-Front retrieves needed configuration from backend during start-up and will be automatically updated if configuration at backend later on changes. After successfully downloading configuration, base_urls defined in local configuration file will be updated according to the backend settings (all backend servers defined will automatically be included in base_urls). A version number will also be included and corresponds with version defined at backend. By looking at version number defined at Plug-Front and Plug-Back, we can verify that configuration is synchronized.
Configuration file <plug-front>/config/config.yml
example:
plugfront:
backend:
base_urls:
- https://<plug-back-one>:9091
- https://<plug-back-two>:9091
password: password
username: plugadm
Plug-Front initial configuration
Plug-Front retrieve full configuration from Plug-Back. Following initial configuration is needed on Plug-Front for establishing connection to Plug-Back:
Config key | Description |
---|---|
plugfront.backend.base_urls | Syntax https://<plug-back-hostname>:<port> . Port must correspond with Plug-Back port configuration. Contains one or more backend servers to be contacted for downloading needed configuration. On startup there must be at least one base_urls defined. After successfully download and configuration, base_urls list will become updated according to the configuration defined by backend, and base_urls should then contain a list of all backend servers. plugfront.backend.version will also be updated and corresponds with version defind at backend. There is built-in logic for load balancing and failover |
plugfront.backend.password | Clear text password will become encrypted on start-up and must correspond with password defined at plug-back |
plugfront.backend.username | Username must corresponding with username defined at plug-back |
plugfront.loglevel | error, info or debug - optional and if defined it will override corresponding configuration retrieved from plug-back |
plugfront.log_to_console | true or false - optional and if defined it will override corresponding configuration retrieved from plug-back |
Using environments
Configuration may also be set by environments.
Syntax is variable names like above dotted notation, but starting with PLUGSSO_
, all uppercase, and underscore instead of dots.
Example (linux using “export”, windows using “set”):
export PLUGSSO_PLUGFRONT_BACKEND_USERNAME=plugadm
export PLUGSSO_PLUGFRONT_BACKEND_PASSWORD=password
export PLUGSSO_PLUGFRONT_BACKEND_BASEURLS=https://192.168.253.1:9091
Environments may also be defined in a vault file having only the initial vaultfile defined as environment
Example:
export PLUGSSO_VAULTFILE=./config/.vaultfile
file "./config/.vaultfile" content:
PLUGSSO_PLUGFRONT_BACKEND_USERNAME=plugadm
PLUGSSO_PLUGFRONT_BACKEND_PASSWORD=password
PLUGSSO_PLUGFRONT_BACKEND_BASEURLS=https://192.168.253.1:9091,https://192.168.253.20:9091
Plug-Back
Plug-Back is the backend component having PlugSSO configuration that covers both Plug-Back and Plug-Front. All backend nodes should be included in plugback.cluster_nodes
. We should also include a increasing version number. Configuration will be automatically synchronized among all cluster_nodes defined and connected frontends.
Note, when configuration have been changed at a specific Plug-Back node, this node also have to be restarted to have the new configuration activated. The new configuration version will then be automatically synchronized among all Plug-Back and Plug-Front nodes.
Configuration file <plug-back>/config/config.yml
have following main components:
domains:
- domain_name: Domain1
authentications:
- endpoint_name: Google
pre_login:
background_image: ./static/img/logo-google.png
display_name: Login with Google
url_path: /sso/app1
authz_endpoint_name: AD
realms:
- /app1
- /app2
use_authz: true
use_mfa:
method: otp
selections:
- OTP,otp
- U2F/FIDO2,u2f
- U2F/FIDO2 (less),u2f-username-less
use_pre_login: true
endpoint:
oauth:
- config:
endpoint_name: Google
...
directory:
- config:
endpoint_name: AD
...
scimgateway:
- config:
endpoint_name: my-scimgateway
...
local:
- config:
endpoint_name: local-allowlist
allowlist_users:
...
plugback:
cluster_nodes:
- plug-back-one.mycompany.com
- plug-back-two.mycompany.com
...
plugfront:
...
version: 1.0
Endpoint section contains endpoint configuration used for authentication and authorization. There are three types of endpoints: oauth, directory, scimgateway and local. We can define as many as we want, and they must have a unique endpoint_name. This endpoint_name will then be used in the domain section for referring to the specific endpoint.
Domains contains one or more domain having the main SSO configuration. For each domain we have to configure:
- One or more authentications using endpoint_name for referring to the actual authentication endpoint. When having several authentications defined, we also need use_pre_login = true, else the first authentication defined will be used without any pre-login dialog.
- If using authorization (use_authz = true), authz_endpoint_name must be set to a valid endpoint_name defined in the endpoint section
- realms contains a list of valid url paths that can be accessed, and any sub paths will automatically be included. Accessing a url having a path not defined in realms will be denied.
- use_mfa includes a
method
and optionalselections
. method = null, otp, u2f or u2f-username-less which sets default mfa method. If selection is defined, prelogin will present selections in a drop-down listbox as available mfa authentication methods. - use_authz = true, activates authorization
- use_pre_login = true, enables prelogin dialog for selecting where to login. Dialog box will be built dynamically based on what is defined the authentication section and will show background_image and display_name for each option.
Having several domains is useful when we need different types of configuration. For example we have an admin url that only administrators are allowed to access. We could then define a new domain having a realm that includes this url and specify a new authz_endpoint_name that is configured for only allowing administrators e.g. only allowing users having AD group membership = xxx.
Notes regarding domains:
- There are no Single-Sign On between domains. Accessing a realm (url) in Domain1 and then accessing a realm in Domain2, requires a new authentication/authorization for Domain2.
- A realm belongs to one domain. This means, we can’t define the same realm (or sub-paths) in other domains
- realm path will automatically include sub-paths e.g.:
- /app3/abc also includes all sub-paths below “abc” like /app3/abc/123
- / includes everything and will override all other realms defined
plugback section contains misc. Plug-Back related configuration
plugfront section contains misc. Plug-Front related configuration
version is numeric value for this configuration file version (e.g. 1.1 and 2). Note, modifying configuration file and restarting plug-back will automatically update all other plug-back and plug-front servers with the new configuration. We should therefore always increment the version when modifying configuration file. We can then on each plug-back/plug-front see if configuration file versions are in sync. Note, lowering the version number will automatically be reverted back to the highest version number by peer backend servers. If version is not included, it will default to 0.
Configuration example 1
- One domain
- Direct Azure tenant login (no prelogin dialog box for selecting where to login)
- No authorization
- Using OTP multifactor authentication
- Having three plug-back servers
domains:
- authentications:
- endpoint_name: AzureMyCompany
authz_endpoint_name: null
domain_name: Domain1
realms:
- /app1
- /app2
use_authz: false
use_mfa:
method: otp
selections: null
use_pre_login: false
endpoint:
oauth:
- config:
endpoint_name: AzureMyCompany
callback_url: https://<plugsso.mycompany.com>/plug/auth
client_id: azure-application-client-id
client_secret: azure-application-client-secret
provider: azure
tenant: AzureAdMyOrg
plugback:
certificate:
certfile: plug-back.cert
keyfile: plug-back.key
cluster_nodes:
- plug-back-one.mycompany.com
- plug-back-two.mycompany.com
- plug-back-three.mycompany.com
listen: 0.0.0.0
log_to_console: false
loglevel: error
password: password
port: 9091
username: plugadm
plugfront:
allowlist_domains:
allowlist_geo_locations:
allowlist_users:
cookie:
httponly: true
maxage: 0
samesite_mode: none
secure: true
displayname: PlugSSO
headers:
jwt:
maxage: 240
log_to_console: false
loglevel: error
oidc_provider:
smtp:
host: smtp.mycompany.com
proxy:
password: password
port: 587
username: [email protected]
version: 1.1
Configuration example 2
Same as example 1, but includes:
- using prelogin: Azure, Google and local AD login
- using built-in directory authorization (not through SCIM Gateway). Also verify that user exist in Active Directory as a normal account, is enabled and password not expired
- claims attributes will become header variables included in the destination communication.
domains:
- authentications:
- endpoint_name: AzureMyCompany
pre_login:
background_image: ./static/img/logo-mycompany.png
display_name: Login with MyCompany
url_path: /app1
- endpoint_name: Google
pre_login:
background_image: ./static/img/logo-google.png
display_name: Login with Google
url_path: /app1
- endpoint_name: AD
pre_login:
url_path: /app2
authz_endpoint_name: AD
domain_name: Domain1
realms:
- /app1
- /app2
use_authz: true
use_mfa:
method: otp
selections: null
use_pre_login: true
endpoint:
oauth:
- config:
endpoint_name: AzureMyCompany
callback_url: https://<plugsso.mycompany.com>/plug/auth
client_id: azure-application-client-id
client_secret: azure-application-client-secret
provider: azure
tenant: AzureAdMyOrg
- config:
endpoint_name: Google
auth_url: https://accounts.google.com/o/oauth2/auth
callback_url: https://<plugsso.mycompany.com>/plug/auth
client_id: google-client-id
client_secret: google-client-secret
preferreddomain: gmail.com
provider: google
directory:
- config:
endpoint_name: AD
base_dn: CN=Users,DC=mycompany,DC=com
mail_attr: mail
search_filter: (&(objectClass=person)(|(sAMAccountName={username})(mail={username}))(|(useraccountcontrol=512)(useraccountcontrol=66048)))
urls:
- ldaps://<ad-domain-controller-one>:636
- ldaps://<ad-domain-controller-two>:636
user_dn: CN=plugsso,CN=Users,DC=mycompany,DC=com
user_password: password
plugback:
certificate:
certfile: plug-back.cert
keyfile: plug-back.key
cluster_nodes:
- plug-back-one.mycompany.com
- plug-back-two.mycompany.com
- plug-back-three.mycompany.com
listen: 0.0.0.0
log_to_console: false
loglevel: error
password: password
port: 9091
username: plugadm
plugfront:
allowlist_domains:
allowlist_geo_locations:
allowlist_users:
cookie:
httponly: true
maxage: 0
samesite_mode: none
secure: true
displayname: PlugSSO
headers:
claims:
- sAMAccountName
- given_name
jwt:
maxage: 240
log_to_console: false
loglevel: error
oidc_provider:
smtp:
host: smtp.mycompany.com
proxy:
password: password
port: 587
username: [email protected]
version: 1.2
Configuration example 3
Same as example 2, but includes:
- Two domains
- Domain1 having multifactor prelogin selections (named as: OTP, U2F/FIDO2 and U2F/FIDO2 (less))
- Domain2 using same Azure tenant login as Domain1, and includes local login through SCIM Gateway
- Domain2 using Authorization through SCIM Gateway
domains:
- authentications:
- endpoint_name: AzureMyCompany
pre_login:
background_image: ./static/img/logo-mycompany.png
display_name: Login with MyCompany
url_path: /sso/app1
- endpoint_name: Google
pre_login:
background_image: ./static/img/logo-google.png
display_name: Login with Google
url_path: /sso/app1
- endpoint_name: AD
pre_login:
url_path: /sso/app2
authz_endpoint_name: AD
domain_name: Domain1
realms:
- /app1
- /app2
use_mfa:
method: otp
selections:
- OTP,otp
- U2F/FIDO2,u2f
- U2F/FIDO2 (less),u2f-username-less
use_pre_login: true
- authentications:
- endpoint_name: AzureMyCompany
pre_login:
background_image: ./static/img/logo-mycompany.png
display_name: Login with MyCompany
url_path: /app3
- endpoint_name: plug-scimgateway
pre_login:
display_name: Internal login
url_path: /app3
authz_endpoint_name: plug-scimgateway
domain_name: Domain2
realms:
- /app3
- /app4
use_mfa:
method: otp
selections: null
use_pre_login: true
endpoint:
oauth:
- config:
endpoint_name: AzureMyCompany
callback_url: https://<plugsso.mycompany.com>/plug/auth
client_id: azure-application-client-id
client_secret: azure-application-client-secret
provider: azure
tenant: AzureAdMyOrg
- config:
endpoint_name: Google
auth_url: https://accounts.google.com/o/oauth2/auth
callback_url: https://<plugsso.mycompany.com>/plug/auth
client_id: google-client-id
client_secret: google-client-secret
preferreddomain: gmail.com
provider: google
directory:
- config:
endpoint_name: AD
base_dn: CN=Users,DC=mycompany,DC=com
mail_attr: mail
search_filter: (&(objectClass=person)(|(sAMAccountName={username})(mail={username}))(|(useraccountcontrol=512)(useraccountcontrol=66048)))
urls:
- ldaps://<ad-domain-controller-one>:636
- ldaps://<ad-domain-controller-two>:636
user_dn: CN=plugsso,CN=Users,DC=mycompany,DC=com
user_password: password
scimgateway:
- config:
endpoint_name: plug-scimgateway
method: post
password: password
path: /plugsso/api
urls:
- http://<scimgateway-host1>:8890
- http://<scimgateway-host2>:8890
user: gwadmin
plugback:
certificate:
certfile: plug-back.cert
keyfile: plug-back.key
cluster_nodes:
- plug-back-one.mycompany.com
- plug-back-two.mycompany.com
- plug-back-three.mycompany.com
listen: 0.0.0.0
log_to_console: false
loglevel: error
password: password
port: 9091
username: plugadm
plugfront:
allowlist_domains:
allowlist_geo_locations:
allowlist_users:
cookie:
httponly: true
maxage: 0
samesite_mode: none
secure: true
displayname: PlugSSO
headers:
claims:
- sAMAccountName
- given_name
jwt:
maxage: 240
log_to_console: false
loglevel: error
oidc_provider:
smtp:
host: smtp.mycompany.com
proxy:
password: password
port: 587
username: [email protected]
version: 1.3
Configuration example 4
Example showing the use of built-in OpenID Connect provider:
- One domain
- General Oauth OIDC login (previous examples showed simplified Azure and Google)
- Local login using SCIM Gateway
- Authorization through SCIM Gateway
- Using multifactor authentication
- Having three plug-back servers
- Built-in OpenID Provider, see section oidc_provider. Also note that we need the callback url that is used by the remote client to be included in the realm definition. This is to allow PlugSSO authentication if not already authenticated.
domains:
- authentications:
- endpoint_name: SomeOidcProvider
pre_login:
background_image: ./static/img/logo-mycompany.png
display_name: Login with MyCompany
url_path: /app1
- endpoint_name: plug-scimgateway
pre_login:
display_name: Internal login
url_path: /app1
authz_endpoint_name: plug-scimgateway
domain_name: Domain1
realms:
- /app1
- /app2
- /auth/system1/callback
- /auth/system2/callback
use_mfa:
method: otp
selections: null
use_pre_login: true
endpoint:
oauth:
- config:
endpoint_name: SomeOidcProvider
provider: oidc
auth_url: https://<host/oauth2/v1/authorize>.
callback_url: https://<plug-front-hostname>/plug/auth
client_id: <client-id>.
client_secret: <client-secret>.
scopes:
- openid
- email
- profile
token_url: https://<host/oauth2/v1/token>.
user_info_url: https://<host/oauth2/v1/userinfo>.
scimgateway:
- config:
endpoint_name: plug-scimgateway
method: post
password: password
path: /plugsso/api
urls:
- http://<scimgateway-host1>:8890
- http://<scimgateway-host2>:8890
user: gwadmin
plugback:
certificate:
certfile: plug-back.cert
keyfile: plug-back.key
cluster_nodes:
- plug-back-one.mycompany.com
- plug-back-two.mycompany.com
- plug-back-three.mycompany.com
listen: 0.0.0.0
log_to_console: false
loglevel: error
password: password
port: 9091
username: plugadm
plugfront:
allowlist_domains:
allowlist_geo_locations:
allowlist_users:
cookie:
httponly: true
maxage: 0
samesite_mode: none
secure: true
displayname: PlugSSO
headers:
claims:
- scim_groups
jwt:
maxage: 240
log_to_console: false
loglevel: error
oidc_provider:
oidc_configs:
- callback_url: https://<company1.com/auth/system1/callback>.
client_id: this_is_client_id1
client_secret: this_is_client_secret1
- callback_url: https://<company2.com/auth/system2/callback>.
client_id: this_is_client_id2
client_secret: this_is_client_secret2
signing_certificate:
certfile: ecdsa.cert
keyfile: ecdsa.key
smtp:
host: smtp.mycompany.com
proxy:
password: password
port: 587
username: [email protected]
version: 1.4
Plug-Back configuration options
plug-back configuration also includes plug-front. plug-front retrieves this configuration from plug-back.
Config key | Description |
---|---|
domains | contains one or more domain having the main SSO configuration |
domains.domain_name | uniqe name of the domain |
domains.authentications | contains one or more authentication options to be used |
domains.authentications.endpoint_name | referres to the endpoint_name defined in endpoint section that will be used by the authentication process |
domains.authentications.pre_login | pre-login dialog box configuration |
domains.authentications.pre_login.background_image | image file to be displayed |
domains.authentications.pre_login.display_name | name to be displayed with the image file |
domains.authentications.pre_login.url_path | where to send user after successful login in case user do access the prelogin page directly (plug/prelogin ). Value must be a valid realm in current domain |
domains.authz_endpoint_name | referres to the endpoint_name defined in endpoint section that will be used by the authorization process |
domains.realms | realms contains a list of valid url paths that can be accessed, also automatically including sub paths. Accessing a url having a path not defined in realms will be denied |
domains.mfa | multifactor authentication configuration |
domains.mfa.method | null, otp, u2f or u2f-username-less sets default mfa method (null means mfa not in use). otp is one-time-password using authenticator app. u2f is universal 2nd factor FIDO2/WebAuthn. u2f-username-less is u2f in both password-less and username-less mode. |
domains.mfa.selections | array of <selecion name>,<method> . If defined, prelogin wil have a drop-down selection box letting user select which mfa to be used. <slection name> is what user will see, and <method> is the method to be used and must be one of the supported mfa.methods |
domains.use_authz | true or false. true, activates authorization |
domains.use_pre_login | true or false. true, enables prelogin dialog for selecting where to login. Dialog box will be built dynamically based on what is defined in the authentication section and will show background_image and display_name for each option. Internal logon having user input fields for username/password will be shown for none oauth endpoint e.g. type endpoint.directory and endpoint.scimgateway (only one internal logon is supported) |
endpoint | contains needed endpoint configuration used for authentication and authorization. There are three types of endpoints: oauth, directory and scimgateway. We can define as many as we want, and they must have a unique endpoint_name. This endpoint_name will then be used in the domain section for referring to a specific endpoint |
endpoint.oauth | contains one or more oauth endpoint configuration |
endpoint.oauth.config | configuration section |
endpoint.oauth.config.provider | oicd, azure, google or github. oidc is the general OpenID Connect configuration while azure and google are built-in having simplified configuration |
endpoint.oauth.config.xxx | for oidc, azure, google and github provider configuration, see dedicated chapter in this document |
endpoint.directory | contains one or more directory endpoint configuration |
endpoint.directory.config | configuration section |
endpoint.directory.config.endpoint_name | unique endpoint name |
endpoint.directory.config.base_dn | where to start ldap search |
endpoint.directory.config.mail_attr | attribute containing users mail address. If no mail-address and using directory for authentication, user will be forced to enter a mail-address during the OTP registration process (because of self-service recovery code) |
endpoint.directory.config.search_filter | search filter that should return one user only. If using authorization, filter should also include needed authorization logic. PlugSSO will replace {username} with user login name, normally this would be mail-address or it could be UserId when using local login e.g.(&(objectClass=person)(|(sAMAccountName={username})(mail={username}))(|(useraccountcontrol=512)(useraccountcontrol=66048))) |
endpoint.directory.config.urls | one or more ldap/ldaps connection urls. When more than one is defined, there will be failover logic in the sequence defined. |
endpoint.directory.config.user_dn | user dn having directory read access to all users |
endpoint.directory.config.user_password | directory user password. Clear text password that will become encrypted on start-up |
endpoint.scimgateway | contains one or more scimgateway endpoint configuration |
endpoint.scimgateway.config | configuartion section |
endpoint.scimgateway.config.endpoint_name | unique endpoint name |
endpoint.scimgateway.config.method | get or post. Must correspond with custom SCIM Gateway plugin. Plug SSO will always send a body containing known user attributes. If password attribute is included, it means plugin also have to authenticate user including any authorization |
endpoint.scimgateway.config.path | SCIM Gateway url path e.g. /api or using baseEntity /plugsso/api . Must correspond with custom SCIM Gateway plugin |
endpoint.scimgateway.config.urls | SCIM Gateway base url. When more than one is defined, there will be failover logic in the sequence defined |
endpoint.scimgateway.config.user | SCIM Gateway admin user |
endpoint.scimgateway.config.password | SCIM Gateway admin user password. Clear text password will become encrypted on start-up |
endpoint.local | contains one or more local configuration used for authorizing users based on allowlist_users settings, not used for authentication |
endpoint.local.config | configuartion section |
endpoint.local.config.endpoint_name | unique endpoint name |
endpoint.local.config.allowlist_users | array of one or more username that are authorized |
plugback | backend configuration: plug-back (current server) |
plugback.certificate | certificate configuration for tls communication. Note, certificate will be autogenerated if not defined |
plugback.certificate.certfile | certificate file name. File must be located in <plug-back/config/certs |
plugback.certificate.keyfile | certificate private key file name. File must be located in <plug-back/config/certs |
plugback.cluster_nodes | a list of all plug-back servers (FQDN) that will be in sync and also used by plug-front |
plugback.listen | listen port, Plug-Front communicates with Plug-Back using this port |
plugback.log_to_console | true or false. true or false. When true, logging to console instead of file, and we can use stdout/stderr redirect if needed |
plugback.loglevel | error, info or debug. Logs to <plug-back>/logs/plug-front.log having file rollover at 10 MB and keeping 10 files |
plugback.password | clear text password will become encrypted (note, corresponding plugfront.backend.password) |
plugback.port | default 9091, plug-front communicates with plug-back using this port (note, corresponding port in plugfront.backend.base_urls) |
plugback.port_cluster_sync | default plugback.port + 1 (9092), port number for cluster synchronization between plug-back servers |
plugback.username | user name (note, corresponding plugfront.backend.username) |
plugfront | frontend configuration: Plug-Front |
plugfront.allowlistdomains | one or more mail-domains. If defined, users having mail-address with a domain that is not included in this list will be denied access e.g: MyCompany.com will only allow access to users having mail address <user>@MyCompany.com |
plugfront.allowlist_geo_locations | array of one or more two-letter country codes. If defined, clients having ip-address that do not correspond to defined country code will be denied access. Use country code PRIVATE to include 127.0.0.1 and none public ip-addresses. Note that nginx needs following configuration for including client ip-address in the PlugSSO communication: proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; . This product includes GeoLite2 data file created by MAXMIND
. Geolocation data file must be located at Plug-Back config/geolocation/geo.mmdb . This file is a copy of GeoLite2-Country.mmdb. Using the commercial GeoIP2 provides a higher-level api |
plugfront.allowlist_users | one or more login names. If defined, users having login name not defined in this list will be denied access e.g. [email protected] will only allow access to peter |
plugfront.cookie | cookie configuration |
plugfront.cookie.httponly | true or false, default true |
plugfront.cookie.maxage | default 0, number of minutes until cookie expires and user have to re-authenticate. Default 0 to delete the cookie when the browser is closed |
plugfront.cookie.samesite_mode | none, lax or strict, default none |
plugfront.cookie.secure | true or false, default false |
plugfront.displayname | product name shown in all user dialogs |
plugfront.headers | header configuration |
plugfront.headers.claims | comma separated list of claims attributes that will be assigned values if they are included by OAuth or local authentication/authorization. Nginx then having rules for including these claims attributes as header variables |
plugfront.jwt | JSON webtoken containing user information attached to cookie |
plugfront.jwt.maxage | default 240, number of minutes until jwt expires. User have to re-authenticate when expired. Should be less than cookie.maxage (if cookie.maxage > 0) |
plugfront.log | log configuration |
plugfront.log.console | true or false. When true, logging to console instead of file, and stdout/stderr redirect can then be used |
plugfront.log.level | error, info or debug. Logs to <plug-front>/logs/plug-front.log having file rollover at 10 MB and keeping 10 files |
plugfront.listen | default 0.0.0.0 accept traffic from all internal interfaces |
plugfront.port | default 9091, the port used by Plug-Front and reverse proxy (nginx) communicates with Plug-Front using this port |
plugfront.oidc_provider | OpenID Connect provider configuration |
plugfront.oidc_provider.oidc_configs | one or more client configuration |
plugfront.oidc_provider.oidc_configs.callback_url | client callback url. This url must also be included as a realm |
plugfront.oidc_provider.oidc_configs.client_id | self defined client id |
plugfront.oidc_provider.oidc_configs.client_secret | self defined client secret |
plugfront.oidc_provider.signing_certificate | ES256 or RS256 signing certificate. Note, certificate will be autogenerated if not defined |
plugfront.oidc_provider.signing_certificate.certfile | certificate file name. File must be located in <plug-front/config/certs |
plugfront.oidc_provider.signing_certificate.keyfile | certificate private key file name. File must be located in <plug-front/config/certs |
plugfront.smtp | mail configuaration. Mail is used for mail address verification and self-service otp reset code |
plugfront.smtp.host | mail server e.g. “smtp.office365.com” |
plugfront.smtp.proxy | if using mailproxy e.g. “http://proxy-host:1234”. Proxy user/password set to same as smtp.user/password |
plugfront.smtp.password | clear text password will become encrypted |
plugfront.smtp.port | port used by mailserver e.g. 587, 25 or 465 |
plugfront.smtp.username | mail account for authentication and also the sender of the email, e.g. “[email protected] ” |
Using environments
Configuration may also be set by using environments.
Syntax is variable names like above dotted notation, but starting with PLUGSSO_
, all uppercase, and underscore instead of dots.
Example (linux using “export”, windows using “set”):
export PLUGSSO_PLUGBACK_USERNAME=plugadm
export PLUGSSO_PLUGBACK_PASSWORD=password
export PLUGSSO_PLUGBACK_CLUSTERNODES=192.168.253.20
Environments may also be defined in a vault file having only the initial vaultfile defined in environment
Example:
export PLUGSSO_VAULTFILE=./config/.vaultfile
file "./config/.vaultfile" content:
PLUGSSO_PLUGBACK_USERNAME=plugadm
PLUGSSO_PLUGBACK_PASSWORD=password
PLUGSSO_PLUGBACK_CLUSTERNODES=192.168.253.20
OIDC General (provider=oidc)
A general OIDC configuration can be used by having provider: oidc
In previous examples providers like azure
and google
have been used
oauth:
- config:
endpoint_name: SomeOidcProvider
provider: oidc
auth_url: https://<host/oauth2/v1/authorize>.
callback_url: https://<plugsso.mycompany.com>/plug/auth
client_id: <client-id>.
client_secret: <client-secret>.
scopes:
- openid
- email
- profile
token_url: https://<host/oauth2/v1/token>.
user_info_url: https://<host/oauth2/v1/userinfo>.
Config key | Description |
---|---|
endpoint.oauth.config.endpoint_name | unique endpoint name |
endpoint.oauth.config.provider | oidc, when using general OIDC configuration |
endpoint.oauth.config.auth_url | external provider authentication url |
endpoint.oauth.config.callback_url | Plug-Front callbak url, should be: https://<plugsso.mycompany.com>/plug/auth |
endpoint.oauth.config.client_id | external provider client id |
endpoint.oauth.config.client_secret | external provider client secret |
endpoint.oauth.config.scopes | openid, email, profile |
endpoint.oauth.config.token_url | external provider token url |
endpoint.oauth.config.user_info_url | external provider userinfo url |
ADFS (provider=adfs)
- config:
auth_url: https://<adfs-host>/adfs/oauth2/authorize/
callback_url: https://<plugsso.mycompany.com>/plug/auth
client_id: <client-id>.
client_secret: <client-secret>.
endpoint_name: ADFS
provider: adfs
scopes: null
token_url: https://<adfs-host>/adfs/oauth2/token/
Azure
Azure configuration portal: https://portal.azure.com
- Azure Active Directory - App registrations - New registration
- Name = PlugSSO, Account types = Single tenant (same as AzureAdMyOrg), Redirect URI =
https://<plugsso.mycompany.com>/plug/auth
- Branding: Microsoft logon dialog box may have a logo. Logo can be added by uploading a file containing the logo
- Authentication: Redirect URIs and account types can be changed
- API permissions: Microsoft Graph - openid (Type = Delegated, Description = Sign users in)
- Certificates & secrets: Create a “New client secret”.
- Copy generated secret
- Copy Overview - Application id
- Secret and Application id must be defined in corresponding Plug-Back
provider azure
configuration asclient_secret
andclient_id
Note, Redirect
URI must match corresponding callback_url
defined in the PlugSSO configuration.
Note, account types can also be defined by editing the manifest file having one of following:
“signInAudience”: “AzureAdMyOrg”
“signInAudience”: “AzureAdMultipleOrgs”
“signInAudience”: “AzureADandPersonalMicrosoftAccount” ¨
Azure PlugSSO configuration (provider=azure)
oauth:
- config:
callback_url: https://<plugsso.mycompany.com>/plug/auth
client_id: <azure-client-id>.
client_secret: <azure-client-secret>.
endpoint_name: Microsoft
provider: azure
tenant: AzureAdMyOrg
Config key | Description |
---|---|
endpoint.oauth.config.provider | azure, when using Azure |
endpoint.oauth.config.tenant | AzureAdMyOrg, AzureAdMultipleOrgs or AzureAdAndPersonalMicrosoftAccount. Note, this definition must correspond with your Azure account configuration. AzureADMyOrg: Only accounts in the organizational directory where the app is registered (single-tenant, your company). AzureADMultipleOrgs: Accounts in any organizational directory (multi-tenant, all companies in Microsoft Azure). AzureADandPersonalMicrosoftAccount: Accounts in any organizational directory (multi-tenant) and personal Microsoft accounts (@outlook.com, @hotmail.com, @live.com) |
Google Cloud Platform: https://console.cloud.google.com
- APIs & Services - Credentials
- Click CREATE CREDENTIALS, then select OAuth client ID
- Application type = Web application
- Name = PlugSSO
- Authorized JavaScript origins
- URIs 1 = https://my-company.com
- URIs 2 = https://localhost
- Authorized redirect URIs
- URIs 1 = https://my-company.com/plug/auth
- URIs 2 = https://localhost/plug/auth
- Click CREATE
- Copy generated “Client ID” and “Client Secret”
- Client ID and Secret must be defined in corresponding Plug-Back
provider google
configuration asclient_id
andclient_secret
Note, URIs 2 localhost is for testing purpose. Origins
URI must match users application origin host URI. Redirect
URI must match corresponding callback_url
defined in the PlugSSO configuration.
Google PlugsSSO configuration (provider=google)
- config:
auth_url: https://accounts.google.com/o/oauth2/auth
callback_url: https://<plugsso.mycompany.com>/plug/auth
client_id: <google-client-id>.
client_secret: <google-client-secret>.
endpoint_name: Google
preferreddomain: gmail.com
provider: google
Config key | Description |
---|---|
endpoint.oauth.config.preferreddomain | domain suffix to be added automatically by Google provided login dialog |
endpoint.oauth.config.provider | google, when using Google |
GitHub (provider=github)
- config:
auth_url: https://github.com/login/oauth/authorize
callback_url: https://<plugsso.mycompany.com>/plug/auth
client_id: <google-client-id>.
client_secret: <google-client-secret>.
endpoint_name: GitHub
provider: github
SCIM Gateway
SCIM Gateway can be used for advanced authentication and authorization logic
For installation see: https://github.com/jelhub/scimgateway
plugin-api is supported using get or post method. For testing, the plugin should be running in debug to analyze what beeing sent by PlugSSO.
Note, in example 3 and 4 scimgateway configuration is using
path: /plugsso/api
This means baseEntity=plugsso
and theapi
is supported by plugin-api. We may use this baseEntity in plugin-api.config for additional endpoint configuration e.g. we might need the hostname, adminuser and adminpassword for connecting a spesific endpoint for handling the PlugSSO authentication and authorization request.
Following is an example of plug-api supporting PlugSSO:
plugin-api.config - port used must correspond with PlugSSO configuration:
{
"scimgateway": {
"port": 8890,
...
}
plugin-api.js - updated scimgateway.postApi method for supporting PlugSSO:
// =================================================
// postApi
// =================================================
//
// post http://localhost:8890/plugsso/api
// body example:
// {
// endpoint_name: 'plug-scimgateway',
// user: { userName: 'bjensen', name: 'Bjarne Jensen', email: '', password: 'mypassword' },
// claims: [ 'email', 'number', 'isTrue', 'myArr' ]
// }
//
// endpoint_name = PlugSSO config endpoint_name used
// user = PlugSSO userobject
// user.userName is mandatory and should always be included, rest is optional
// user.password is set for Authentication request and blank for Authorization request
//
// if Authentication request and user.email is blank, we should retrieve email from endpoint (if supported)
// and return email attribute with users mail address (even though claims does not contain attribute email)
//
// claims contains attributes to be returned if they are supported by endpoint.
// Note, claims are plugfront.header.claims attributes. These are genaral and may belong to OAuth, Directory and SCIM Gateway
// So, they may not belong to SCIM Gateway
//
// if user not authenticated/authorized OK, then throw error that includes the text "Not Authorized"
// having that text tells PlugSSO to use ban-counter and also to bring up a new logon again with an error message.
//
// returning claims example:
// {
// "email": "[email protected]"
// }
//
scimgateway.postApi = async (baseEntity, apiObj) => {
const action = 'postApi'
scimgateway.logger.debug(`${pluginName} handling "${action}" apiObj=${JSON.stringify(apiObj)}`)
try {
if (!baseEntity && baseEntity !== 'plugsso') {
throw new Error(`Invalid ${action} baseEntity`)
}
// authenticate/authorizate user
// if password is included user must be authenticated
// logic needed here....
// example returning OK if password=password
let authenticated = false
let authorized = false
if (apiObj.user && typeof apiObj.user.password !== 'undefined') { // password included
if (apiObj.user.username && apiObj.user.password && apiObj.user.password === 'password') authenticated = true
} else {
if (apiObj.user && apiObj.user.username) authorized = true
}
if (!authenticated && !authorized) throw new Error('Not Authorized')
// user authorized
return null
// example returning claims:
// return { email: '[email protected]', number: 1234, isTrue: true, myArr: ["aaa", "bbb"] }
} catch (err) {
const newErr = err
throw newErr
}
}
Nginx
Nginx must be configured to validate all requests through PlugSSO. Any claim headers returned by PlugSSO can also be included as headers to the final forwarded destination.
Configuration must include the "Mandatory Start/End section"
shown in example below. In addition, each proxy location
section that forwards to destinatin application must include the PlugSSO authentication at the very beginning: auth_request /plug/validate;
For testing purposes the Nginx configuration example shown below is also configured to run a webserver at port 8899 that responds with a message to simulate application access. The location section defines /sso
to be protected by PlugSSO and forwards to this internal webserver at port 8899. If using url’s like https://server-name/sso/app1 or https://server-name/sso/app2 and these are defined as realms in PlugSSO configuration (/sso/app1 and /sso/app2), users will be authenticated/authorized by PlugSSO.
Note, example is using TLS and a certificate is required (self-signed certificate can be used)
Nginx configuration example:
# example: https://localhost/sso/app1
worker_processes 1;
error_log logs/error.log;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server_tokens off;
# load balancing
upstream plug-front {
# all plug-front servers to be included
# plug-front should be running locally and remote "backup" will only be used in case local is unavailable
server 127.0.0.1:9090;
server 127.0.0.1:9090 backup;
}
upstream app-srv {
# all app-srv servers to be included without backup definition
server 127.0.0.1:8899;
server 127.0.0.1:8899;
}
server {
# redirect to 443 https
listen 80 default_server;
server_name _;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
ssl_protocols TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
# current directory is <nginx-path>/conf
ssl_certificate cert/server.cert;
ssl_certificate_key cert/server.key;
server_name _;
root /var/www/html/;
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
# === Mandatory START ===
location = /plug/validate {
# PlugSSO validation of request (returns 401 or 200)
proxy_pass http://plug-front/validate?url=$scheme://$http_host$request_uri;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
}
# if validate returns `401 not authorized`, forward request to the error401 block for login
error_page 401 = @error401;
location @error401 {
return 302 $scheme://$http_host/plug/login?url=$scheme://$http_host$request_uri;
}
location /plug {
rewrite ^/plug/(.*) /$1 break;
proxy_pass http://plug-front;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# === Mandatory END ===
location /sso {
# PlugSSO authentication/authorization before forward to destination
auth_request /plug/validate;
# forward to local static webserver for testing purposes
rewrite ^/sso/(.*) /$1 break;
proxy_pass http://app-srv;
# Example adding user_id header based on uid claim returned by PlugSSO
# "error_log logs/error.log debug;" will list X_Plug_Claims_<PlugSSO claim attribute> in the debug log
auth_request_set $user_id $upstream_http_X_Plug_Claims_Uid;
proxy_set_header user_id $user_id;
}
}
server {
# testing purpose
listen 8899;
server_name _;
root /var/www/html/;
# return a message for all locations
add_header Content-Type text/plain;
return 200 "Yes, you are authorized for: $request_uri";
}
}