What was verified
Verified on the Email channel, with no changes on the Veriqa side.
GitLab
The barrier. Out of the box the sign-in fails:omniauth_openid_connect puts scope into the
grant_type=authorization_code request, where
RFC 6749 §4.1.3 does not define it. Veriqa
answers invalid_request and GitLab reports:
args:
sub. GitLab treats the
address from the email claim as already confirmed and sends no verification mail of its own.
When no
email claim arrives, GitLab does not refuse: it creates the user, grants a session and
fills in a service address of its own, shaped like
temp-email-for-oauth-{login}@gitlab.localhost, while prompting the user to complete the profile.Keycloak
Here Veriqa is attached to another identity provider as an external OIDC provider: Keycloak accepts the sign-in from Veriqa and then acts as the provider for its own applications. There is barely any configuration. Keycloak imports the Veriqa discovery document and fills in every address itself,jwks_uri included, and turns on signature validation on its own.
The barrier is the last name, not the email. The realm user profile requires email,
firstName and lastName by default. Veriqa emits family_name only when the channel supplies a
last name: a messaging channel takes it from the user’s profile, where it is optional, and Email
never supplies one. So the first sign-in stops at the “Update Account Information” form — always on
the Email channel, and for some users on the others.
What to do. Realm settings → User profile: clear the required flag on firstName and
lastName.
Two completion forms in a row. When a sign-in lacks a claim that Veriqa collects itself, Veriqa
asks the user for it on its own form before handing the sign-in back. A relying party with a profile
completion form of its own — Keycloak, and Logto as well — then shows a second form right after it.
For such an application turn the Veriqa form off with ClaimCompletionFormEnabled: false in its
entry of Veriqa:OpenIddict:Clients (see Clients). With the form
off, the missing claims are not issued and do not block the sign-in: the relying party asks for them
on its own form. The phone number and the email are not affected — Veriqa obtains them through
separate mechanisms.
Grafana
The barrier. With noemail claim Grafana does not refuse outright — it goes looking for the
address by a GitHub-compatible convention, requesting {api_url}/emails, which does not exist in
OpenID Connect. It gets a 404 and stops the authentication:
email arrives. The Email channel provides it; on a channel that carries
no address (Telegram) Veriqa collects it inside the sign-in when email is required, or your own
code supplies one — see Getting the phone number and email.
The account. Created automatically, with email, name and login all set to the address.
Grafana sends no
nonce and does not verify the id_token signature against JWKS — it reads the
claims straight from the token body. It also tries to parse the Veriqa access token as a JWT and
logs token is not in JWT format: the access token is not a JWT — today it is an opaque reference
token, and at the time of the run it was a JWE — and this does not affect sign-in.Nextcloud
Its barriers are in the transport rather than in the claims, and both are cleared by Nextcloud settings. HTTPS only. Over plain HTTPuser_oidc returns a 404 page reading “You must access Nextcloud
with HTTPS to use OpenID Connect”. The check passes over HTTPS, in debug mode, or with the
allow_insecure_http app setting — the last one is meant for an internal network.
Private-address protection. Until allow_local_remote_servers=true is set, Nextcloud will not
reach an issuer on a private network. It is hard to diagnose: the return leg answers 403 and the only
trace is a line in nextcloud.log:
If you intend to match Nextcloud accounts to your own by
sub, clear uniqueUid before the
first sign-in: changing the setting later does not rename users who already exist.
Nextcloud verifies the
id_token signature against JWKS and does not call
/connect/userinfo at all — every claim comes from the id_token. Claims that Veriqa serves only
from userinfo will not reach it; enable enrichLoginIdTokenWithUserinfo if you need them.Discourse
Theopenid_connect plugin is built into core — there is nothing to install. There are two
barriers, and both are cleared by forum settings.
By default Discourse does not request an email. The openid_connect_authorize_scope
setting contains only openid. For a forum that is a dead end: without an address no account is
created and the sign-in stops at the registration form. Set it to openid email profile.
Private-address protection. While the issuer sits on a private network Discourse will not
reach it: allowed_internal_hosts must list its host. The browser meanwhile receives a generic
openid_connect_discovery_error — the cause is visible only with openid_connect_verbose_logging
turned on. In production the barrier does not arise.
What is verified and what is not. Signing in an existing user works, the association is written
against the Veriqa sub, and Discourse verifies the id_token signature against JWKS. Creating a
new account is not verified: Discourse performs it in a separate request from the browser,
guarded by anti-bot checks.
Odoo
The bundled module does not work. The stockauth_oauth only speaks the implicit flow: it
asks for response_type=token, sends no nonce, and verifies no signature. Veriqa answers
unsupported_response_type: the implicit flow was removed from
OAuth 2.1 as unsafe. From the
outside it looks like an error page instead of a return to the site.
What to install. The auth_oidc module from the OCA
repository (the branch matching your Odoo version). In the provider settings choose
flow = OpenID Connect (authorization code flow) and fill in the fields that appear: Token URL,
JWKS URL and Client Secret. The JWKS URL is what lets Odoo verify the Veriqa signature — and it
does.
The account. Created automatically — free sign-up is on by default in a fresh Odoo database,
so nothing extra needs enabling. The link is written against the Veriqa sub in the oauth_uid
field and the address becomes the login. When no address arrives, Odoo makes up a synthetic login
of its own and still creates the user.
Moodle
Moodle has no OIDC client of its own; the one in use is theauth_oidc plugin Microsoft ships for Entra ID. Sign-in
works, the user is created and the link is written against the Veriqa sub — but three barriers
have to be cleared, and two of them you will meet in production as well.
The plugin sends a parameter meant for another provider. In every mode except “Microsoft Identity Platform”
it adds a resource parameter to the request, and when the setting is left empty it substitutes a
hardcoded Microsoft Graph address. Veriqa answers invalid_target: Veriqa registers no
RFC 8707 resources, so no value passes.
There are two ways through, and the one on the Veriqa side needs no change in Moodle: enable the
per-client quirk drop-resource-parameter for this client_id
and the parameter is stripped before validation. Nothing consumes it here, so the issued token is
the one a client that never sent it would get — no resource is registered and no audience granted.
Protection against private addresses — and against ports. Moodle blocks outbound requests by
address (curlsecurityblockedhosts) and also by port: curlsecurityallowedport allows only 443
and 80 by default. Unlike the other systems, this barrier does not go away by itself in
production — if your issuer listens on a non-standard port, that port has to be allowed
explicitly. The diagnostics are thin: the browser shows “Error in OpenID Connect. Please check
logs”, and the real cause (The URL is blocked.) appears only after the plugin’s debug mode is
switched on.
A colon in the identifier. If Moodle has nothing but the Veriqa sub to build a username
from, sign-in stops at “The given username contains invalid characters”: the sub looks like
telegram:1234 or email:address, and Moodle does not accept a colon in usernames. Two ways out:
request the profile scope, so the name comes from preferred_username, or enable
extendedusernamechars.
After creation. Moodle sends the new user to a “complete your profile” page: first and last
name are mandatory there, and a channel does not always supply them.
The email requirement: four different behaviors
Telegram carries no address, and systems treat its absence differently. The difference is practical: it decides whether you need a substituted address at all.
For the same picture across CMS engines, see the CMS integrations overview.
Local testing: every system has its own switch
When you run both on your own machine the issuer ends up on a private network, and almost every system guards against calls there with a flag of its own:
The first four barriers disappear in production along with the public issuer. Moodle is the
exception: it also restricts the port, so it remains an obstacle on a public address too when the
issuer listens on something other than 443 or 80. More on local test setups in
Local testing.
Not verified
- Channels other than Email. On Telegram the expected difference is the address requirement (see the table above).
- Merging with an account already present locally — apart from Discourse, every system above was verified with a newly created user.
- Roles, groups and permissions. The user gets the default role; mapping groups from claims was not tested in any of the systems.
- Repeat sign-in and profile refresh from the provider on subsequent sign-ins.