WebsitesConnects directly

Sign in with NobleID on WordPress

Add Sign in with NobleID to a WordPress site with the OpenID Connect Generic Client plugin.

Written for
The OpenID Connect Generic Client plugin (slug daggerhart-openid-connect-generic), version 3.11.3 or later. Earlier versions have no Issuer field, so every NobleID sign-in fails: update first.
Tested
We checked the plugin's requests against a test server. The plugin itself has not been run against NobleID.

Register on NobleID

  1. In Dashboard → OAuth apps, register a confidential app. You need two-step login on your NobleID to do this.
  2. Add this redirect URI, with your own host:
    https://example.org/wp-admin/admin-ajax.php?action=openid-connect-authorize
    Copy the value the plugin's settings page shows. With Alternate Redirect URI on, it is https://example.org/openid-connect-authorize instead.
  3. Tick these scopes: OpenID (always on), Profile profile, Email email. The platform asks for these, and sign-in stops if it asks for a scope the app does not have.
  4. Copy the client ID and the client secret. The secret is shown once.

The app works at once. Until NobleID verifies it, people see a “not verified” notice on the consent screen.

Plugin settings

In Settings → OpenID Connect Client:

FieldValue
Login TypeOpenID Connect button on login form
Client IDnbcl_your_client_id
Client Secret Keynbcs_your_client_secret (or set it in wp-config.php, below)
OpenID Scopeopenid email profile
Login Endpoint URLhttps://www.nobleid.org/oauth/authorize
Userinfo Endpoint URLhttps://id.nobleid.org/oauth/userinfo
Token Validation Endpoint URLhttps://id.nobleid.org/oauth/token
End Session Endpoint URLleave empty to end only the WordPress session, or https://www.nobleid.org/oauth/logout to end the NobleID session too (see below)
JWKS URIhttps://id.nobleid.org/.well-known/jwks.json
Issuerhttps://id.nobleid.org
ACR valuesleave empty
Identity Keysub
Nickname Keypreferred_username
Email Formatting{email}
Display Name Formatting{name}
Identify with User Nameunticked
Enable Refresh Tokenunticked
Disable SSL Verifyunticked
Allow Internal IDPunticked
Link Existing Usersunticked (see below)
Create user if does not existyour choice

Set the Issuer field. The login endpoint is on www.nobleid.org, but the issuer is https://id.nobleid.org. With the Issuer field empty, the plugin takes the issuer from the login endpoint's host and every sign-in fails.

Set the JWKS URI. The plugin always checks the ID token's iss, aud and exp. Only with the JWKS URI does it also check the signature. Do not run without it.

The plugin can also import settings from https://id.nobleid.org/.well-known/openid-configuration (Import from Discovery Document). The imported values are not saved until you click Save Changes. Then check that Issuer and JWKS URI are filled. The import can also fill End Session Endpoint URL: empty it unless you want sign-out to end the NobleID session too.

To keep the secret out of the database, define it in wp-config.php:

wp-config.php
define( 'OIDC_CLIENT_ID', getenv( 'NOBLEID_CLIENT_ID' ) );
define( 'OIDC_CLIENT_SECRET', getenv( 'NOBLEID_CLIENT_SECRET' ) );

Existing WordPress users

With Link Existing Users ticked, the plugin matches an existing account by email address. It does not check email_verified first, so someone could put an administrator's address on a NobleID account and sign in as that administrator. Keep it unticked and link existing accounts by hand.

The plugin records the NobleID sub on the WordPress user and finds returning users by it. The Identity Key only picks the username for new accounts. sub is unique and never changes. preferred_username reads better, but it can change and can collide with an existing username.

What the plugin does

  • It is a confidential client using client_secret_post and state. It sends no PKCE and no nonce. NobleID allows both for confidential clients.
  • It sends an extra scope parameter on the token request. NobleID ignores it.
  • It does not check the iss response parameter. It checks the ID token's iss against the Issuer field.
  • With End Session Endpoint URL empty, signing out ends the WordPress session only. With it set, the plugin also sends the person to NobleID, which asks them to confirm, then returns them to the page WordPress shows after sign-out.

With the login button, that page is https://example.org/wp-login.php?loggedout=true&wp_lang=en_US, and the language part follows each user's language. NobleID returns people only to an exact address listed in the app's sign-out return URLs, and otherwise shows an error. Set one fixed address with a small plugin, and list it in the NobleID dashboard:

wp-content/mu-plugins/nobleid-logout.php
<?php
// Return everyone to the home page after sign-out. List exactly this address
// (with the trailing slash) as a sign-out return URL on your NobleID app.
// Priority below 99, the plugin's own filter, which then sends it to NobleID.
add_filter( 'logout_redirect', function () {
    return home_url( '/' );
}, 10 );

Fill the profile from NobleID

NobleID claimWordPress fieldScope
given_nameFirst Nameprofile
family_nameLast Nameprofile
preferred_usernameNickname (Nickname Key)profile
nameDisplay name (Display Name Formatting {name})profile
emailEmail (Email Formatting {email})email
websiteWebsite, with the code belowprofile
orcidA user meta field, with the code beloworcid

The plugin fills the first five when it creates the account. On each later sign-in it calls the openid-connect-generic-update-user-using-current-claim action with the claims, so a small plugin can fill what is still empty:

For orcid, add orcid to the plugin's OpenID Scope field and tick ORCID iD on your app.

wp-content/plugins/nobleid-profile/nobleid-profile.php
<?php
/*
 * Plugin Name: NobleID profile fields
 * Description: Fills empty profile fields from Sign in with NobleID. Never overwrites what the person edited.
 */
add_action( 'openid-connect-generic-update-user-using-current-claim', function ( $user, $claims ) {
    if ( empty( $user->user_url ) && ! empty( $claims['website'] ) ) {
        wp_update_user( array( 'ID' => $user->ID, 'user_url' => esc_url_raw( $claims['website'] ) ) );
    }
    // Sent only when the person proved the iD by signing in to ORCID.
    if ( ! empty( $claims['orcid'] ) && ! empty( $claims['orcid_verified'] ) && ! get_user_meta( $user->ID, 'orcid', true ) ) {
        update_user_meta( $user->ID, 'orcid', esc_url_raw( $claims['orcid'] ) );
    }
}, 10, 2 );

Every claim, and how to fill a profile without overwriting what people edit: Fill your app's profile from NobleID.

Before you go live

  • Sign in once with your own NobleID. People need a first and last name on their NobleID profile to sign in; NobleID asks for it if it is missing.
  • Key accounts on sub. Link to an existing account by email only when email_verified is true.
  • Remove any localhost redirect URIs from the app.
  • Keep the client secret out of version control.

NobleID values

Issuer
https://id.nobleid.org
Discovery URL
https://id.nobleid.org/.well-known/openid-configuration
Authorization endpoint
https://www.nobleid.org/oauth/authorize
Token endpoint
https://id.nobleid.org/oauth/token
Userinfo endpoint
https://id.nobleid.org/oauth/userinfo
JWKS URL
https://id.nobleid.org/.well-known/jwks.json
Revocation endpoint
https://id.nobleid.org/oauth/revoke
End-session endpoint
https://www.nobleid.org/oauth/logout
ID token signingES256

Sources

Something here does not match what you see? Email info@nobleid.org.

    ByNobleID