[Bug]: JWT Authentication issues
What happened?
I have configured dab with JWT authentication/authorization:
"host": {
"mode": "production",
"authentication": {
"provider": "Custom",
"jwt": {
"audience": "System",
"issuer": "https://my.authentication.com"
}
}
}
I cannot get the token validated.
Token contains
roles: ["authenticated"]
it seems similar to: https://github.com/Azure/data-api-builder/discussions/2364
Version
1.5.56
What database are you using?
PostgreSQL
What hosting model are you using?
Custom Docker host
Which API approach are you accessing DAB through?
REST
Relevant log output
Request starting HTTP/1.1 GET http://************************/api/u_da_hrm_u_selskap?$first=10 - - -
info: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler[1]
Failed to validate the token.
Microsoft.IdentityModel.Tokens.SecurityTokenInvalidIssuerException: IDX10204: Unable to validate issuer. validationParameters.ValidIssuer is null or whitespace AND validationParameters.ValidIssuers is null or empty.
at Microsoft.IdentityModel.Tokens.Validators.ValidateIssuerAsync(String issuer, SecurityToken securityToken, TokenValidationParameters validationParameters, BaseConfiguration configuration)
at Microsoft.IdentityModel.Tokens.Validators.ValidateIssuer(String issuer, SecurityToken securityToken, TokenValidationParameters validationParameters, BaseConfiguration configuration)
at Microsoft.IdentityModel.Tokens.InternalValidators.ValidateAfterSignatureFailed(SecurityToken securityToken, Nullable`1 notBefore, Nullable`1 expires, IEnumerable`1 audiences, TokenValidationParameters validationParameters, BaseConfiguration configuration)
at Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.ValidateSignature(JsonWebToken jwtToken, TokenValidationParameters validationParameters, BaseConfiguration configuration)
at Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.ValidateSignatureAndIssuerSecurityKey(JsonWebToken jsonWebToken, TokenValidationParameters validationParameters, BaseConfiguration configuration)
at Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.ValidateJWSAsync(JsonWebToken jsonWebToken, TokenValidationParameters validationParameters, BaseConfiguration configuration)
info: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler[7]
Bearer was not authenticated. Failure message: IDX10204: Unable to validate issuer. validationParameters.ValidIssuer is null or whitespace AND validationParameters.ValidIssuers is null or empty.
fail: Microsoft.AspNetCore.Server.Kestrel[13]
Connection id "0HNEU9DJLM514", Request id "0HNEU9DJLM514:00000001": An unhandled exception was thrown by the application.
System.InvalidOperationException: No authentication handler is registered for the scheme 'OAuthAuthentication'. The registered schemes are: Bearer. Did you forget to call AddAuthentication().Add[SomeAuthHandler]("OAuthAuthentication",...)?
at Microsoft.AspNetCore.Authentication.AuthenticationService.AuthenticateAsync(HttpContext context, String scheme)
at Azure.DataApiBuilder.Core.AuthenticationHelpers.ClientRoleHeaderAuthenticationMiddleware.InvokeAsync(HttpContext httpContext) in /_/src/Core/AuthenticationHelpers/ClientRoleHeaderAuthenticationMiddleware.cs:line 76
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Azure.DataApiBuilder.Service.Startup.<>c__DisplayClass19_0.<<Configure>b__3>d.MoveNext() in /_/src/Service/Startup.cs:line 538
--- End of stack trace from previous location ---
at Azure.DataApiBuilder.Core.Services.PathRewriteMiddleware.InvokeAsync(HttpContext httpContext) in /_/src/Core/Services/PathRewriteMiddleware.cs:line 89
at Azure.DataApiBuilder.Core.Services.CorrelationIdMiddleware.Invoke(HttpContext httpContext) in /_/src/Core/Services/CorrelationIdMiddleware.cs:line 53
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application)
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
Request finished HTTP/1.1 GET http://*****************/api/u_da_hrm_u_selskap?$first=10 - 500 0 - 16.5796ms
Code of Conduct
- [x] I agree to follow this project's Code of Conduct
@JerryNixon: Here is my output. I will have to redact some output:
Call:
curl -v -H "Authorization: Bearer eyJraWQiOi...Yw" https://dab-***/api/u_da_hrm_u_histhjem
* Host dab-***:443 was resolved.
* IPv6: (none)
* IPv4: 34.***.***.***, 34.***.***.***
* Trying 34.***.***.***:443...
* schannel: disabled automatic use of client certificate
* ALPN: curl offers http/1.1
* ALPN: server accepted http/1.1
* Connected to dab-*** (34.***.***.***) port 443
* using HTTP/1.x
> GET /api/u_da_hrm_u_histhjem HTTP/1.1
> Host: dab-***
> User-Agent: curl/8.14.1
> Accept: */*
> Authorization: Bearer eyJraWQiOi...Yw
>
* Request completely sent off
* schannel: remote party requests renegotiation
* schannel: renegotiating SSL/TLS connection
* schannel: SSL/TLS connection renegotiated
< HTTP/1.1 500 Internal Server Error
< Content-Length: 0
< Date: Fri, 22 Aug 2025 12:58:33 GMT
< Server: Kestrel
<
* Connection #0 to host dab-*** left intact
Token:
{
"kid": "7ad2fc5e-6a63-4754-a6d3-2ec6585e908a",
"alg": "RS256"
}.{
"aud": "System",
"sub": "dab",
"scope": "GETmodule:read",
"roles": [
"authenticated"
],
"iss": "https://auth-***",
"exp": 1755867668,
"iat": 1755866768,
"customer": "demo"
}.{
"e": "AQAB",
"kty": "RSA",
"n": "4iL0VTnvqbns-bWtyw4tUFWOK5sOndg_dYFRkkicGeSweKbMV2UcnRsMAxgmWmJjAjS2TWR0bFIGc3mVnXRQ4hhZkMOeb4vW0C7GIrZ5kjgEEIkvYDaqNr-x5H2oJQcYE2UWy-AZ0R-UxVQQt_Vbb-GWqntLJBI8QKlA6caKOmqJ5c3ynGqrWDT6-pLARyUrZhDcMK2J445COzKQOsNBJFfUb8fwbkHY5svgac3Y0mdSdXjDWD6eej3X4AuNBBBCVjlF_gHuTOM6UW47Bg5WZny_xo3LbYt58TyCMGnsuwnSE7foLnDSxc220j-cdwDAUSVivKYADN5w02a3dI3ljw"
}
OpenID Configuration retrieved from https://auth-***/.well-known/openid-configuration:
{
"issuer": "https://auth-***",
"authorization_endpoint": "https://auth-***/authz/api/authorization",
"userinfo_endpoint": "https://auth-***/authz/api/user_info",
"jwks_uri": "https://auth-***/.well-known/openid-configuration/jwks",
"response_types_supported": [
"token"
],
"subject_types_supported": [
"public"
],
"id_token_signing_alg_values_supported": [
"RS256"
],
"grant_types_supported": [
"client_credentials",
"refresh_token"
]
}
JWKS Configuration is:
{
"keys": [
{
"kty": "RSA",
"e": "AQAB",
"kid": "7ad2fc5e-6a63-4754-a6d3-2ec6585e908a",
"n": "4iL0VTnvqbns-bWtyw4tUFWOK5sOndg_dYFRkkicGeSweKbMV2UcnRsMAxgmWmJjAjS2TWR0bFIGc3mVnXRQ4hhZkMOeb4vW0C7GIrZ5kjgEEIkvYDaqNr-x5H2oJQcYE2UWy-AZ0R-UxVQQt_Vbb-GWqntLJBI8QKlA6caKOmqJ5c3ynGqrWDT6-pLARyUrZhDcMK2J445COzKQOsNBJFfUb8fwbkHY5svgac3Y0mdSdXjDWD6eej3X4AuNBBBCVjlF_gHuTOM6UW47Bg5WZny_xo3LbYt58TyCMGnsuwnSE7foLnDSxc220j-cdwDAUSVivKYADN5w02a3dI3ljw"
}
]
}
Dab config:
{
"data-source": {
"database-type": "postgresql",
"connection-string": "Host=@env('DB_HOST');Port=5432;Username=@env('DB_USER');Password=@env('DB_PASSWORD');Database=@env('DB_USER');Timeout=60;Command Timeout=0;"
},
"runtime": {
"rest": {
"enabled": true
},
"graphql": {
"enabled": true
},
"host": {
"mode": "production",
"authentication": {
"provider": "Custom",
"jwt": {
"audience": "System",
"issuer": "https://dab-***"
}
}
}
},
"entities": {
"u_da_invoicing_u_faktdok": {
"source": {
"object": "u_da.invoicing_u_faktdok",
"type": "view",
"key-fields": [
"u_pk"
]
},
"rest": {
"methods": [
"GET"
]
},
"permissions": [
{
"role": "authenticated",
"actions": [
"read"
]
},
{
"role": "anonymous",
"actions": [
"read"
]
}
]
},
...
}
}
Dab logs:
Information: Microsoft.DataApiBuilder 1.5.56
Information: User provided config file: /opt/dab-config.json
Loading config file from /opt/dab-config.json.
Information: Loaded config file: /opt/dab-config.json
Information: Setting default minimum LogLevel: Error for Production mode.
Starting the runtime engine...
Loading config file from /opt/dab-config.json.
Monitoring config: /opt/dab-config.json for hot-reloading.
warn: Microsoft.AspNetCore.DataProtection.Repositories.EphemeralXmlRepository[50]
Using an in-memory repository. Keys will not be persisted to storage.
warn: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[59]
Neither user profile nor HKLM registry available. Using an ephemeral key repository. Protected data will be unavailable when application exits.
info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[58]
Creating key {d01bca85-3230-4605-b8fc-88abc595b103} with creation date 2025-08-22 12:37:10Z, activation date 2025-08-22 12:37:10Z, and expiration date 2025-11-20 12:37:10Z.
warn: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[35]
No XML encryptor configured. Key {d01bca85-3230-4605-b8fc-88abc595b103} may be persisted to storage in unencrypted form.
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://[::]:5000
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
Content root path: /opt
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
Request starting HTTP/1.1 GET http://dab-***/api - - -
warn: Microsoft.AspNetCore.HttpsPolicy.HttpsRedirectionMiddleware[3]
Failed to determine the https port for redirect.
fail: Microsoft.AspNetCore.Server.Kestrel[13]
Connection id "0HNF1F427DFP1", Request id "0HNF1F427DFP1:00000001": An unhandled exception was thrown by the application.
System.InvalidOperationException: No authentication handler is registered for the scheme 'OAuthAuthentication'. The registered schemes are: Bearer. Did you forget to call AddAuthentication().Add[SomeAuthHandler]("OAuthAuthentication",...)?
at Microsoft.AspNetCore.Authentication.AuthenticationService.AuthenticateAsync(HttpContext context, String scheme)
at Azure.DataApiBuilder.Core.AuthenticationHelpers.ClientRoleHeaderAuthenticationMiddleware.InvokeAsync(HttpContext httpContext) in /_/src/Core/AuthenticationHelpers/ClientRoleHeaderAuthenticationMiddleware.cs:line 76
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Azure.DataApiBuilder.Service.Startup.<>c__DisplayClass19_0.<<Configure>b__3>d.MoveNext() in /_/src/Service/Startup.cs:line 538
--- End of stack trace from previous location ---
at Azure.DataApiBuilder.Core.Services.PathRewriteMiddleware.InvokeAsync(HttpContext httpContext) in /_/src/Core/Services/PathRewriteMiddleware.cs:line 89
at Azure.DataApiBuilder.Core.Services.CorrelationIdMiddleware.Invoke(HttpContext httpContext) in /_/src/Core/Services/CorrelationIdMiddleware.cs:line 53
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application)
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
Request finished HTTP/1.1 GET http://dab-***/api - 500 0 - 60.2469ms
Config generation script (run in the entrypoint of the Docker container):
import psycopg2
import json
import os
import re
from collections import defaultdict
# Get database connection details from environment variables
DB_CONFIG = {
"dbname": os.getenv("DB_NAME", "postgres"),
"user": os.getenv("DB_USER", "postgres"),
"password": os.getenv("DB_PASSWORD", "postgres"),
"host": os.getenv("DB_HOST", "localhost"),
"port": int(os.getenv("DB_PORT", 5432))
}
# Schemas to expose (comma-separated environment variable)
SCHEMAS = [_.strip() for _ in os.getenv("DB_SCHEMAS", "public").split(",")]
EXCLUDED = [_.strip() for _ in os.getenv("DB_EXCLUDED", "").split(",")]
MODE = os.getenv("MODE", "production")
JWT_ISSUER = f"{os.getenv('TENANT_FQDN', 'https://localhost')}"
LOG_LEVEL = os.getenv("LOG_LEVEL", "info").lower()
postgresql_to_dab = {
# Boolean
"boolean": "bool",
# Number - Integers
"smallint": "number",
"integer": "number",
"bigint": "number",
# Number - Decimals
"decimal": "number",
"numeric": "number",
"real": "number",
"double precision": "number",
"money": "number",
# String
"text": "string",
"character varying": "string",
"varchar": "string",
"character": "string",
"char": "string",
"citext": "string",
# Other types considered as String
"uuid": "string",
"json": "string",
"jsonb": "string",
"xml": "string",
}
# Connect to PostgreSQL
conn = psycopg2.connect(**DB_CONFIG)
objs = defaultdict(list)
# Query all tables/views from the selected schemas
for schema in SCHEMAS:
cur = conn.cursor()
cur.execute(f"""
SELECT
table_name AS object_name,
'TABLE' AS object_type,
'' AS parameters
FROM information_schema.tables
WHERE table_schema = '{schema}'
AND table_type = 'BASE TABLE'
UNION
SELECT
table_name AS object_name,
'VIEW' AS object_type,
'' AS parameters
FROM information_schema.views
WHERE table_schema = '{schema}'
UNION
SELECT
p.proname AS object_name,
'FUNCTION' AS object_type,
pg_get_function_arguments(p.oid) as parameters
FROM
pg_catalog.pg_namespace n
JOIN pg_catalog.pg_proc p ON p.pronamespace = n.oid
WHERE
p.prokind = 'p'
AND
n.nspname = '{schema}';
""")
for t in cur.fetchall():
if t[1] == 'FUNCTION':
_params = [re.split(r"\s+", x.strip()) for x in t[2].split(',')]
params = {}
for p in _params:
print(p)
params[p[1]]=postgresql_to_dab.get(p[2], "string")
objs[schema].append({'name':t[0], 'type':t[1], 'parameters': params})
else:
objs[schema].append({'name':t[0], 'type':t[1]})
cur.close()
# jobid integer, jobtype character varying, response json
print("🔍 Found tables/views:", objs)
conn.close()
# Build DAB config
dab_config = {
"data-source": {
"database-type": "postgresql",
"connection-string": "Host=@env('DB_HOST');Port=5432;Username=@env('DB_USER');Password=@env('DB_PASSWORD');Database=@env('DB_USER');Timeout=60;Command Timeout=0;"
},
"runtime": {
"rest": {
"enabled": True
},
"graphql": {
"enabled": True
},
"host": {
"mode": MODE,
"authentication": {
"provider": "Custom",
"jwt": {
"audience": "System",
"issuer": JWT_ISSUER
}
}
}
},
"entities": {},
}
if MODE == 'development':
dab_config["runtime"]["host"]["authentication"] = {
"provider": "Simulator"
}
for schema in objs:
for obj in objs[schema]:
print(f"📦 Adding entity: {schema}.{obj['name']}")
entity_name = f"{schema}_{obj['name']}"
if entity_name in EXCLUDED or obj['name'] == "flyway_schema_history" or obj['name'] == "u_migration_marker":
print(f"Skipping excluded entity: {schema}.{obj['name']}")
continue
entity_type = obj['type']
if entity_type == 'VIEW':
dab_config["entities"][entity_name] = {
"source": {
"object": f"{schema}.{obj['name']}",
"type": "view",
"key-fields": ["u_pk"]
},
"rest": {
"methods": ["GET"]
},
"permissions": [
{
"role": "authenticated",
"actions": ["read"]
},
{
"role": "anonymous",
"actions": ["read"]
}
]
}
if entity_type == 'TABLE':
dab_config["entities"][entity_name] = {
"source": {
"object": f"{schema}.{obj['name']}",
"type": "view",
"key-fields": ["u_pk"]
},
"rest": {
"methods": ["GET","POST", "PUT", "DELETE"]
},
"permissions": [
{
"role": "authenticated",
"actions": ["read", "create", "update", "delete"]
},
{
"role": "anonymous",
"actions": ["read"]
}
]
}
# else:
# dab_config["entities"][entity_name] = {
# "source": {
# "object": f"{schema}.{obj['name']}",
# "type": "stored-procedure",
# "parameters": obj['parameters']
# },
# "rest": {
# "methods": ["POST"] # Functions usually require POST for input
# },
# "permissions": [
# {"role": "authenticated", "actions": ["execute"]}
# ]
# }
# Save to file
with open("dab-config.json", "w") as f:
json.dump(dab_config, f, indent=2)
print("✅ Generated dab-config.json with all tables/views from schemas:", SCHEMAS)
@JerryNixon: I have checked that the token is correctly forwarded in the request by placing httpbun instead the actual container and i can see that I am getting all headers correctly:
{
"method": "GET",
"args": {},
"headers": {
"Accept": "application/json, text/plain, */*",
"Accept-Encoding": "gzip, compress, deflate, br",
"Authorization": "Bearer eyJraWQiOi...fMG_g",
"Host": "dab-***",
"Request-Start-Time": "1755875420645",
"User-Agent": "bruno-runtime/2.9.1",
"X-Forwarded-Access-Token": "eyJraWQiOiI...fMG_g",
"X-Forwarded-Email": "me",
"X-Forwarded-For": "172.16.191.65, 172.16.174.174",
"X-Forwarded-Host": "dab-***",
"X-Forwarded-Port": "80",
"X-Forwarded-Proto": "http",
"X-Forwarded-Server": "traefik-66d7b78c46-shzx7",
"X-Forwarded-User": "me",
"X-Real-Ip": "172.16.191.65"
},
"origin": "127.0.0.1",
"url": "http://dab-***/get",
"form": {},
"data": "",
"json": null,
"files": {}
}
@JerryNixon @vs-dsva is there any update on this issue? I also cannot get the Custom provider. I'm using v1.5.56 and getting the No authentication handler is registered for the scheme 'OAuthAuthentication' error.
dab-config:
"host": {
"authentication": {
"provider": "Custom",
"jwt": {
"audience": "project_id",
"issuer": "https://api.descope.com/project_id",
"jwks-uri": "https://api.descope.com/project_id/.well-known/jwks.json"
}
},
"mode": "development"
},
warn: Microsoft.AspNetCore.HttpsPolicy.HttpsRedirectionMiddleware[3]
Failed to determine the https port for redirect.
fail: Microsoft.AspNetCore.Server.Kestrel[13]
Connection id "0HNFNJI639R64", Request id "0HNFNJI639R64:00000001": An unhandled exception was thrown by the application.
System.InvalidOperationException: No authentication handler is registered for the scheme 'OAuthAuthentication'. The registered schemes are: StaticWebAppsAuthentication, Bearer, SimulatorAuthentication. Did you forget to call AddAuthentication().Add[SomeAuthHandler]("OAuthAuthentication",...)?
at Microsoft.AspNetCore.Authentication.AuthenticationService.AuthenticateAsync(HttpContext context, String scheme)
at Azure.DataApiBuilder.Core.AuthenticationHelpers.ClientRoleHeaderAuthenticationMiddleware.InvokeAsync(HttpContext httpContext) in /_/src/Core/AuthenticationHelpers/ClientRoleHeaderAuthenticationMiddleware.cs:line 76
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Azure.DataApiBuilder.Service.Startup.<>c__DisplayClass19_0.<<Configure>b__3>d.MoveNext() in /_/src/Service/Startup.cs:line 538
--- End of stack trace from previous location ---
at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
at Azure.DataApiBuilder.Core.Services.PathRewriteMiddleware.InvokeAsync(HttpContext httpContext) in /_/src/Core/Services/PathRewriteMiddleware.cs:line 89
at Azure.DataApiBuilder.Core.Services.CorrelationIdMiddleware.Invoke(HttpContext httpContext) in /_/src/Core/Services/CorrelationIdMiddleware.cs:line 53
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application)