It's like OpenProject::Cache, but it encrypts cached
values at rest. Callers that store confidential things in the cache
have been updated to use it, reducing the risk to expose secrets
to an attacker that obtained access to the contents of OpenProject's cache.
This is the only and official API to register an auth provider.
However, so far it was optional to create a database entry in the
auth_providers table and only OIDC and SAML did that.
On the other hand, we added expectations about auth providers have
a database entry in more and more places of the codebase.
Now making sure that every auth provider is represented in the database.
Allowing to manage SCIM clients through the UI, including
all the authentication methods and their related "behind the scenes"
setup, i.e. service account, oauth application and access tokens.
Due to STI nature it is required. Otherwise undesired behavior is possible
in dev and test environments(where usually lazy loading is enabled).
The udesired behavior can be like:
Fetching not loaded yet STI model through its parent model
(e.g. `User.find(service_account_id)` raises `ActiveRecord::NotFound`, because
`ServiceAccount` has not been referenced yet.
`SubclassRegistry` has been removed, because:
1. `.register_subclasses` and `.registered_subclasses` produce unexpected results.
```ruby
# e.g.: Principal -> User -> ServiceAccount
Principal.register_subclass(User)
# Then
Principal.registered_subclasses == [User] # true
User.registered_subclasses == [User] # true
ServiceAccount.registered_subclasses == [User] # true
# Having User as a subclass of User and ServiceAccount seems to be weird.
```
2. There seems to be no big win in have the additional list of subclasses that have to be manually filled.
3. Used in commit apprach seems to be simpler.
It is just calling STI classes explicitly in to_prepare block of configuration.
Providing a subject_token_type is required according to RFC 8693,
but we left it out so far. New versions of Keycloak fail the token exchange
request if that parameter is missing.
Since this required introducing another urn-constant, I've decided to move all
of them to a common location.
Azure/Entra ID seems to return stringly typed
expiry dates under certain conditions. We'll now
also handle properly readable numeric strings.
Missing expiration times or non-numeric strings,
such as "banana" will be treated as not knowing
the expiration time.
Some files were missing the corresponding comment before.
This change was effectively achieved through:
rubocop -A --only Style/FrozenStringLiteralComment,Layout/EmptyLineAfterMagicComment,Style/RedundantFreeze modules/openid_connect
Other parameters stored in the session are kind of optional,
but without the access token we can't create a user token.
The most popular case for a missing access token should be
that the login happened through a non-OIDC provider.
The hook we are using is firing for all kinds of omniauth
logins. By ensuring that we have a matching OpenIDConnect provider,
we know that we are currently performing an OIDC login, which is
the only case where we want to try storing OIDC tokens.
Previously we didn't consider the expires_in from the
token endpoint response, thus being unable to know whether
a token was expired or not before using it. While we worked
around that by inspecting tokens that seemed readable as JWTs,
the better approach is to memoize the information returned by the
server, as that also allows to know the expiration time of
opaque access tokens.
We'll keep the JWT extraction around as a fallback mechanism,
but prefer reading the info from the token endpoint, if it's present.
Doing so hopefully simplifies token handling a bit.
It's now not required to pass specific sessions into services
as long as a user is passed.
This theoretically also enables us to act in the name of a user from
a background job, though we have no specific plans for that yet.
A possible downside is, that we now require being handed long-term tokens
(i.e. tokens with offline_access scope). On the other hand, we'd have had
to consider keeping our tokens fresh for the previous implementation, which
we also didn't solve yet.
This commit provides an alternative implementation
for storing tokens compared to the parent commit.
The idea is that we will not only need to store access
and refresh tokens obtained via Omniauth, but also the ones
to access third party services that will most likely be obtained
through OAuth 2.0 Token Exchange. This structure allows to store
all of these tokens in the same data model, while keeping the
implementation separated from the back-channel logout logic.
Storing tokens in the database to have them available for
requests to third parties (e.g. Nextcloud) later.
The OIDC session is now marked as optional, since the
session link is also used to store access and refresh tokens
associated with the session. Those tokens might be present,
even if the session id (which belongs to the optional
OIDC Back-Channel Logout specification) is missing.
https://community.openproject.org/work_packages/55643
- Add new warden authentication stategy to handle jwt issued by configured OIDC.
- Modify exisiting doorkeeper_oauth stategy to ignore jwts.
- Fill in WWW-Autheticate header with auth failure information.
- Make keycloak docker dev setup use postgres as a database.
Provides a separate call to Users::UpdateService when an admin attribute
has been mapped to the omniauth info hash.
When updating the admin flag fails for some reason (e.g., trying to remove the last admin)
it will prevent the user from logging in.