Summary
When [server.auth] is configured to validate JWTs against a Microsoft Entra ID (Azure AD) JWKS endpoint, loreserver exits during startup with Error: Internal Error and an unsymbolicated backtrace. No log line indicates the cause. The same configuration starts successfully when the JWKS endpoint is pointed at a different OIDC provider (e.g. Google), which isolates the failure to the contents of Entra's JWKS response rather than the rest of the auth configuration.
Environment
- Lore Server version:
0.8.3+201
- OS: Linux (x86-64)
- Identity provider: Microsoft Entra ID (Azure AD), v2.0 endpoints, RS256-signed tokens
Configuration to reproduce
[server.auth]
jwt_issuer = "https://login.microsoftonline.com/<tenant-id>/v2.0"
jwt_audience = ["<application-client-id>"]
[server.auth.jwk]
endpoint = "https://login.microsoftonline.com/<tenant-id>/discovery/v2.0/keys"
Steps to reproduce
- Configure
[server.auth] as above with a real Entra tenant id, application (client) id, and the tenant's v2.0 JWKS endpoint.
- Start
loreserver.
Expected behavior
The server loads the signing keys from the JWKS endpoint and starts with authentication enabled.
Actual behavior
The server exits during startup. The only error output is:
Error: Internal Error
Stack backtrace:
0: <unknown>
1: <unknown>
...
Run with RUST_LOG=trace and RUST_BACKTRACE=1, the trace confirms the JWKS endpoint is reached and a response is received successfully (TLS completes against the genuine login.microsoftonline.com certificate, the response body is read, and the connection is returned to the pool). The urc.auth.jwk_service meter is created, and the Internal Error is emitted immediately afterward. No warn!/error! line is produced explaining the failure.
Logs
Redacted RUST_LOG=trace / RUST_BACKTRACE=1 excerpt (TLS handshake frames and unrelated startup lines removed; the JWKS fetch completes, then the error is emitted):
The HTTPS fetch succeeds (note pooling idle connection, which only happens after the response body is fully read); the error is emitted immediately after the urc.auth.jwk_service meter is created — i.e. while processing the fetched keys, not while fetching them.
Isolation / control test
Changing only [server.auth.jwk].endpoint to https://www.googleapis.com/oauth2/v3/certs (leaving jwt_issuer/jwt_audience unchanged) allows the server to boot past auth initialization:
Lore Public QUIC Server: Auth: yes
Starting Lore HTTP Server: 0.0.0.0:41339, Auth: yes
Starting Lore GRPC Server: 0.0.0.0:41337, Auth: enabled
This indicates the failure is specific to the JWKS payload returned by Entra, not the surrounding configuration.
Root cause
The failure occurs in lore-server/src/auth/jwk.rs, in JwkServiceImpl::fetch_new_keys(), while iterating the fetched keys:
let algorithm = jwk
.common
.key_algorithm
.ok_or(JWKServiceError::InternalError)?;
jwk.common.key_algorithm corresponds to the JWK alg member, which is treated as required. Microsoft Entra's JWK entries do not include an alg field, so key_algorithm is None and the function returns JWKServiceError::InternalError.
JWKServiceError::InternalError has a Display implementation of the literal string "Internal Error":
#[derive(Error, Debug)]
pub enum JWKServiceError {
#[error("Internal Error")]
InternalError,
...
}
This error is surfaced to the user because the initial key fetch at startup propagates it with ? (in lore-server/src/server.rs, around line 1713):
let jwk_service = JwkServiceImpl::new(jwk.clone());
jwk_service
.fetch_new_keys(None /* fetch all keys */)
.await?;
Unlike the non-success-status and JSON-parse branches earlier in fetch_new_keys (which call warn! before returning), the missing-alg path returns InternalError without logging, so the underlying cause is not visible in the logs.
Observed JWKS field differences
Fields present in a Microsoft Entra v2.0 key object (https://login.microsoftonline.com/<tenant-id>/discovery/v2.0/keys):
| Field |
Present |
kty (RSA) |
yes |
use (sig) |
yes |
kid |
yes |
x5t |
yes |
n, e |
yes |
x5c |
yes |
cloud_instance_name |
yes (Entra-specific) |
issuer |
yes (Entra-specific) |
alg |
no |
Google's keys, by contrast, include "alg": "RS256", which is why pointing the same configuration at Google's endpoint succeeds. Entra's OIDC discovery document advertises id_token_signing_alg_values_supported: ["RS256"].
Summary
When
[server.auth]is configured to validate JWTs against a Microsoft Entra ID (Azure AD) JWKS endpoint,loreserverexits during startup withError: Internal Errorand an unsymbolicated backtrace. No log line indicates the cause. The same configuration starts successfully when the JWKS endpoint is pointed at a different OIDC provider (e.g. Google), which isolates the failure to the contents of Entra's JWKS response rather than the rest of the auth configuration.Environment
0.8.3+201Configuration to reproduce
Steps to reproduce
[server.auth]as above with a real Entra tenant id, application (client) id, and the tenant's v2.0 JWKS endpoint.loreserver.Expected behavior
The server loads the signing keys from the JWKS endpoint and starts with authentication enabled.
Actual behavior
The server exits during startup. The only error output is:
Run with
RUST_LOG=traceandRUST_BACKTRACE=1, the trace confirms the JWKS endpoint is reached and a response is received successfully (TLS completes against the genuinelogin.microsoftonline.comcertificate, the response body is read, and the connection is returned to the pool). Theurc.auth.jwk_servicemeter is created, and theInternal Erroris emitted immediately afterward. Nowarn!/error!line is produced explaining the failure.Logs
Redacted
RUST_LOG=trace/RUST_BACKTRACE=1excerpt (TLS handshake frames and unrelated startup lines removed; the JWKS fetch completes, then the error is emitted):The HTTPS fetch succeeds (note
pooling idle connection, which only happens after the response body is fully read); the error is emitted immediately after theurc.auth.jwk_servicemeter is created — i.e. while processing the fetched keys, not while fetching them.Isolation / control test
Changing only
[server.auth.jwk].endpointtohttps://www.googleapis.com/oauth2/v3/certs(leavingjwt_issuer/jwt_audienceunchanged) allows the server to boot past auth initialization:This indicates the failure is specific to the JWKS payload returned by Entra, not the surrounding configuration.
Root cause
The failure occurs in
lore-server/src/auth/jwk.rs, inJwkServiceImpl::fetch_new_keys(), while iterating the fetched keys:jwk.common.key_algorithmcorresponds to the JWKalgmember, which is treated as required. Microsoft Entra's JWK entries do not include analgfield, sokey_algorithmisNoneand the function returnsJWKServiceError::InternalError.JWKServiceError::InternalErrorhas aDisplayimplementation of the literal string"Internal Error":This error is surfaced to the user because the initial key fetch at startup propagates it with
?(inlore-server/src/server.rs, around line 1713):Unlike the non-success-status and JSON-parse branches earlier in
fetch_new_keys(which callwarn!before returning), the missing-algpath returnsInternalErrorwithout logging, so the underlying cause is not visible in the logs.Observed JWKS field differences
Fields present in a Microsoft Entra v2.0 key object (
https://login.microsoftonline.com/<tenant-id>/discovery/v2.0/keys):kty(RSA)use(sig)kidx5tn,ex5ccloud_instance_nameissueralgGoogle's keys, by contrast, include
"alg": "RS256", which is why pointing the same configuration at Google's endpoint succeeds. Entra's OIDC discovery document advertisesid_token_signing_alg_values_supported: ["RS256"].