SSO Security
Technical specification of login through an external identity provider, prepared for security and IT teams evaluating Q247 before deployment. The configuration itself, that is the fields to fill in and the actions on the identity provider's side, is described in SSO (OIDC).
Standard and algorithms
Q247 implements SSO login based on OpenID Connect 1.0, in the Authorization Code Flow. Every identity provider compliant with this standard is supported, including Microsoft Entra ID, Okta, Google and Keycloak.
| Element | Solution |
|---|---|
| Session tokens | JWT |
| Token signing | HS256 (HMAC-SHA-256), an algorithm enforced in the code |
| Client secret encryption | AES-256-GCM with a two-layer key |
| Transport | HTTPS only |
Enforcing a single signing algorithm at the code level means no other one will be accepted during token verification. This eliminates the class of attacks based on algorithm substitution.
Client secret encryption
The OIDC client secret, provided once during configuration, is stored in encrypted form. The key is two-layered:
- DEK (Data Encryption Key): a random 32-byte key, generated once for each secret.
- KEK (Key Encryption Key): the environment's master encryption key, configured at the infrastructure level and stored outside the database.
Decrypting the secret requires access to the KEK, which never reaches the database. A leak of the database alone therefore does not expose the secret in plain form.
Role of the client secret
The OIDC protocol involves two separate acts of identity verification. The first is authenticating the user, which the identity provider carries out at the moment login data is given. The second is authenticating the client application, that is Q247 confirming its own identity to the provider. The client secret serves only the second one.
It is used at one point in the flow: when exchanging the authorization code for a token. The Q247 backend then sends the token endpoint a request containing the received authorization code, the application identifier and the secret. Without a correct secret, the provider rejects the request, and an intercepted authorization code stays useless.
| Property | How it is implemented |
|---|---|
| Server-side only | the secret takes part only in communication from the Q247 backend to the identity provider, and is never sent to the browser |
| Encryption in the database | AES-256-GCM, access to the database does not expose the plain value |
| Short-lived in memory | decrypted for the duration of a single HTTP call to the token endpoint, without being cached |
| Single use of the code | the authorization code works once and expires after a few minutes |
Login flow
The flow runs through the user's browser, with no direct connection from the identity provider to Q247:
- The user opens the Q247 login screen and chooses login through the identity provider.
- The Q247 backend fetches the provider's Discovery Document and builds the authorization address, adding the
stateandnonceparameters. - The user's browser is redirected to the identity provider.
- The user authenticates at the provider, outside Q247.
- The provider redirects the browser back to the Q247 return address, passing the authorization code.
- The Q247 backend verifies the
stateparameter and exchanges the code for a token, authenticating itself with the client secret. - Q247 verifies the ID token, reads the e-mail address from it and maps it to an account in the organization.
- A session is created: an access token in the form of a JWT and a refresh token stored in a cookie.
Safeguards built into the flow
- The
stateparameter: a random token binding the request to the response, protects against CSRF attacks. Single-use, valid for 5 minutes. - The
nonceparameter: a random value placed in the ID token, prevents replaying an earlier response. - HttpOnly and Secure cookie: the refresh token is inaccessible to JavaScript code and sent only over HTTPS.
- Scope restriction: Q247 requests only the
openid,emailandprofilescopes, so it does not gain access to any other data in the identity directory.
Discovery Document and address validation
Every OIDC-compliant provider publishes at a predictable address a JSON document describing its endpoints and capabilities. Q247 fetches it automatically at every login session initiation, thanks to which the whole configuration comes down to providing one address, and the remaining parameters are discovered.
The address is validated at the moment the configuration is saved. It must contain the .well-known/openid-configuration segment and point to a public IP address. Private and local addresses are rejected, which protects against using this field to force requests to internal resources.
Direction of network traffic
In the Authorization Code Flow, the identity provider never establishes a connection to the Q247 server. The authorization code comes back through the user's browser, via an HTTP redirect, and all direct calls between Q247 and the provider are initiated by Q247.
| Connection | Direction | Required |
|---|---|---|
| Q247 backend → provider (fetching the Discovery Document) | outbound from Q247 | yes |
| Q247 backend → provider (exchanging the code for a token) | outbound from Q247 | yes |
| User's browser → provider (login) | via the client | yes |
| User's browser → Q247 (return with the code) | via the client | yes |
| Provider → Q247 backend | none | no |
Enabling SSO does not require letting in any inbound traffic from the identity provider.
See also
- SSO (OIDC): configuration fields and actions on the identity provider's side
- Log in: the login screen from the user's perspective
- Management Portal Security: infrastructure and service access of the application itself
- Enterprise Plugin Security: specification of Q247's second component