Skip to content

Commit e128eb7

Browse files
CopilotJerryNixonCopilotRubenCerna2079Aniruddh25
authored
Introduce new authentication provider Unauthenticated as the default (#3075)
## Why make this change? New authentication provider where all operations run as `anonymous`. Useful when DAB is behind an app gateway or APIM where authentication is handled externally. ## What is this change? **Core Implementation:** - Added `UnauthenticatedAuthenticationHandler` in `UnauthenticatedAuthentication/` folder (follows Simulator pattern) - Added `IsUnauthenticatedAuthenticationProvider()` to `AuthenticationOptions.cs` - Added `IsUnauthenticatedIdentityProvider` property to `RuntimeConfig.cs` - Added explicit mapping in `ClientRoleHeaderAuthenticationMiddleware.ResolveConfiguredAuthNScheme()` - Changed `AuthenticationOptions` record default from `AppService` to `Unauthenticated` - Updated `IsAppServiceIdentityProvider` and `IsStaticWebAppsIdentityProvider` to only return true when explicitly set **CLI & Validation:** - Changed `dab init` default from `AppService` to `Unauthenticated` - `Utils.ValidateAudienceAndIssuerForJwtProvider()` accepts Unauthenticated without JWT config - `ConfigGenerator.IsConfigValid()` emits warning (not error) when used with non-anonymous roles - Simulator provider comparison made case-insensitive (consistent with Unauthenticated check) **Schema:** - Added `Unauthenticated` to `dab.draft.schema.json` provider enum **Test Updates:** - Updated all CLI snapshot files (115+ files) to expect `Unauthenticated` as default - Updated `InitTests.cs`, `UpdateEntityTests.cs`, and `TestHelper.cs` to use `Unauthenticated` as the default provider in unit test setup helpers - Integration test config files (`dab-config.*.json`) continue to use `AppService` because integration tests exercise role-based policies (authenticated role, database policies, exclude fields) that require actual token-based authentication — the `Unauthenticated` provider treats all requests as anonymous and does not process bearer tokens **Key behaviors:** - Allowed in `production` mode (unlike Simulator) - Allowed with `authenticated`/custom role permissions (warning emitted) - No JWT configuration required ## How was this tested? - [x] Integration Tests - Integration test config files (`dab-config.*.json`) retain `AppService` provider to support role-based policy testing - [x] Unit Tests - `ValidateUnauthenticatedProviderIdentification` in `AuthenticationConfigValidatorUnitTests.cs` - Unauthenticated test cases in `TestValidateAudienceAndIssuerForAuthenticationProvider` - Updated all CLI snapshot files (115+ files) to expect `Unauthenticated` as default - `TestUnauthenticatedProviderNonAnonymousRoleDetection` validates warning is emitted for non-anonymous roles ## Sample Request(s) ```bash # Now defaults to Unauthenticated provider dab init --database-type mssql --connection-string "..." # Explicit AppService provider (previous default) dab init --database-type mssql --connection-string "..." --auth.provider AppService ``` Config snippet: ```json { "runtime": { "host": { "authentication": { "provider": "Unauthenticated" } } } } ``` <!-- START COPILOT CODING AGENT TIPS --> --- ✨ Let Copilot coding agent [set things up for you](https://github.com/Azure/data-api-builder/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot) — coding agent works faster and does higher quality work when set up for your repo. --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: JerryNixon <[email protected]> Co-authored-by: Copilot <[email protected]> Co-authored-by: RubenCerna2079 <[email protected]> Co-authored-by: Aniruddh Munde <[email protected]> Co-authored-by: Aniruddh25 <[email protected]> Co-authored-by: souvikghosh04 <[email protected]> Co-authored-by: Souvik Ghosh <[email protected]>
1 parent 94c60e2 commit e128eb7

138 files changed

Lines changed: 387 additions & 145 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

config-generators/cosmosdb_nosql-commands.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
init --config "dab-config.CosmosDb_NoSql.json" --database-type "cosmosdb_nosql" --connection-string "AccountEndpoint=https://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==" --cosmosdb_nosql-database "graphqldb" --cosmosdb_nosql-container "planet" --graphql-schema "schema.gql" --host-mode Development --cors-origin "http://localhost:5000"
1+
init --config "dab-config.CosmosDb_NoSql.json" --database-type "cosmosdb_nosql" --connection-string "AccountEndpoint=https://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==" --cosmosdb_nosql-database "graphqldb" --cosmosdb_nosql-container "planet" --graphql-schema "schema.gql" --host-mode Development --cors-origin "http://localhost:5000" --auth.provider AppService
22
add PlanetAlias --config "dab-config.CosmosDb_NoSql.json" --source "graphqldb.planet" --permissions "anonymous:create,read,update,delete" --graphql "Planet:Planets"
33
update PlanetAlias --config "dab-config.CosmosDb_NoSql.json" --permissions "anonymous:read" --fields.include "*"
44
update PlanetAlias --config "dab-config.CosmosDb_NoSql.json" --permissions "authenticated:create,read,update,delete"

config-generators/dwsql-commands.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
init --config "dab-config.DwSql.json" --database-type dwsql --set-session-context true --connection-string "Server=tcp:127.0.0.1,1433;Persist Security Info=False;User ID=sa;Password=REPLACEME;MultipleActiveResultSets=False;Connection Timeout=5;" --host-mode Development --cors-origin "http://localhost:5000" --rest.request-body-strict true
1+
init --config "dab-config.DwSql.json" --database-type dwsql --set-session-context true --connection-string "Server=tcp:127.0.0.1,1433;Persist Security Info=False;User ID=sa;Password=REPLACEME;MultipleActiveResultSets=False;Connection Timeout=5;" --host-mode Development --cors-origin "http://localhost:5000" --rest.request-body-strict true --auth.provider AppService
22
add Publisher --config "dab-config.DwSql.json" --source publishers --permissions "anonymous:read" --source.key-fields "id"
33
add Stock --config "dab-config.DwSql.json" --source stocks --permissions "anonymous:create,read,update,delete" --source.key-fields "categoryid,pieceid"
44
add stocks_price --config "dab-config.DwSql.json" --source stocks_price --permissions "authenticated:create,read,update,delete" --source.key-fields "categoryid,pieceid,instant"

config-generators/mssql-commands.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
init --config "dab-config.MsSql.json" --database-type mssql --set-session-context true --connection-string "Server=tcp:127.0.0.1,1433;Persist Security Info=False;User ID=sa;Password=REPLACEME;MultipleActiveResultSets=False;Connection Timeout=5;" --host-mode Development --cors-origin "http://localhost:5000" --graphql.multiple-create.enabled true --rest.request-body-strict true
1+
init --config "dab-config.MsSql.json" --database-type mssql --set-session-context true --connection-string "Server=tcp:127.0.0.1,1433;Persist Security Info=False;User ID=sa;Password=REPLACEME;MultipleActiveResultSets=False;Connection Timeout=5;" --host-mode Development --cors-origin "http://localhost:5000" --graphql.multiple-create.enabled true --rest.request-body-strict true --auth.provider AppService
22
add Publisher --config "dab-config.MsSql.json" --source publishers --permissions "anonymous:read"
33
add Publisher_MM --config "dab-config.MsSql.json" --source publishers_mm --graphql "Publisher_MM:Publishers_MM" --permissions "anonymous:*"
44
add Stock --config "dab-config.MsSql.json" --source stocks --permissions "anonymous:create,read,update,delete"

config-generators/mysql-commands.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
init --config "dab-config.MySql.json" --database-type mysql --connection-string "server=localhost;database=datagatewaytest;uid=root;pwd=REPLACEME" --host-mode Development --cors-origin "http://localhost:5000" --rest.request-body-strict true
1+
init --config "dab-config.MySql.json" --database-type mysql --connection-string "server=localhost;database=datagatewaytest;uid=root;pwd=REPLACEME" --host-mode Development --cors-origin "http://localhost:5000" --rest.request-body-strict true --auth.provider AppService
22
add Publisher --config "dab-config.MySql.json" --source publishers --permissions "anonymous:read"
33
add Stock --config "dab-config.MySql.json" --source stocks --permissions "anonymous:create,read,update,delete"
44
add Book --config "dab-config.MySql.json" --source books --permissions "anonymous:create,read,update,delete" --graphql "book:books"

config-generators/postgresql-commands.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
init --config "dab-config.PostgreSql.json" --database-type postgresql --connection-string "Host=localhost;Database=datagatewaytest;username=REPLACEME;password=REPLACEME" --host-mode Development --cors-origin "http://localhost:5000" --rest.request-body-strict true
1+
init --config "dab-config.PostgreSql.json" --database-type postgresql --connection-string "Host=localhost;Database=datagatewaytest;username=REPLACEME;password=REPLACEME" --host-mode Development --cors-origin "http://localhost:5000" --rest.request-body-strict true --auth.provider AppService
22
add Publisher --config "dab-config.PostgreSql.json" --source publishers --permissions "anonymous:read"
33
add Stock --config "dab-config.PostgreSql.json" --source stocks --permissions "anonymous:create,read,update,delete"
44
add Book --config "dab-config.PostgreSql.json" --source books --permissions "anonymous:create,read,update,delete" --graphql "book:books"

schemas/dab.draft.schema.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,10 @@
418418
{
419419
"const": "Custom",
420420
"description": "Custom authentication provider defined by the user. Use the JWT property to configure the custom provider."
421+
},
422+
{
423+
"const": "Unauthenticated",
424+
"description": "Unauthenticated provider where all operations run as anonymous. Use when Data API builder is behind an app gateway or APIM where authentication is handled externally."
421425
}
422426
],
423427
"default": "AppService"

src/Cli.Tests/ConfigureOptionsTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,7 @@ public void TestUpdateCorsAllowCredentialsHostSettings(bool allowCredentialsValu
664664
[DataRow("Appservice", DisplayName = "Update authentication.provider to AppService for Host.")]
665665
[DataRow("azuread", DisplayName = "Update authentication.provider to AzureAD for Host.")]
666666
[DataRow("entraid", DisplayName = "Update authentication.provider to EntraID for Host.")]
667+
[DataRow("Unauthenticated", DisplayName = "Update authentication.provider to Unauthenticated for Host.")]
667668
public void TestUpdateAuthenticationProviderHostSettings(string authenticationProviderValue)
668669
{
669670
// Arrange -> all the setup which includes creating options.

src/Cli.Tests/EndToEndTests.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1122,12 +1122,15 @@ public async Task TestExitOfRuntimeEngineWithInvalidConfig(
11221122
[DataRow("AppService", true)]
11231123
[DataRow("AzureAD", true)]
11241124
[DataRow("EntraID", true)]
1125+
[DataRow("Unauthenticated", true)]
11251126
public void TestBaseRouteIsConfigurableForSWA(string authProvider, bool isExceptionExpected)
11261127
{
11271128
string[] initArgs = { "init", "-c", TEST_RUNTIME_CONFIG_FILE, "--host-mode", "development", "--database-type", "mssql",
11281129
"--connection-string", SAMPLE_TEST_CONN_STRING, "--auth.provider", authProvider, "--runtime.base-route", "base-route" };
11291130

1130-
if (!Enum.TryParse(authProvider, ignoreCase: true, out EasyAuthType _))
1131+
if (!Enum.TryParse(authProvider, ignoreCase: true, out EasyAuthType _) &&
1132+
!authProvider.Equals("Unauthenticated", StringComparison.OrdinalIgnoreCase) &&
1133+
!authProvider.Equals("Simulator", StringComparison.OrdinalIgnoreCase))
11311134
{
11321135
string[] audIssuers = { "--auth.audience", "aud-xxx", "--auth.issuer", "issuer-xxx" };
11331136
initArgs = initArgs.Concat(audIssuers).ToArray();

src/Cli.Tests/InitTests.cs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public Task MsSQLDatabase()
4949
setSessionContext: true,
5050
hostMode: HostMode.Development,
5151
corsOrigin: new List<string>() { "http://localhost:3000", "http://nolocalhost:80" },
52-
authenticationProvider: EasyAuthType.AppService.ToString(),
52+
authenticationProvider: "Unauthenticated",
5353
restPath: "rest-api",
5454
config: TEST_RUNTIME_CONFIG_FILE);
5555

@@ -71,7 +71,7 @@ public Task CosmosDbPostgreSqlDatabase()
7171
setSessionContext: false,
7272
hostMode: HostMode.Development,
7373
corsOrigin: new List<string>() { "http://localhost:3000", "http://nolocalhost:80" },
74-
authenticationProvider: EasyAuthType.AppService.ToString(),
74+
authenticationProvider: "Unauthenticated",
7575
restPath: "/rest-endpoint",
7676
config: TEST_RUNTIME_CONFIG_FILE);
7777

@@ -94,7 +94,7 @@ public Task TestInitializingConfigWithoutConnectionString()
9494
setSessionContext: false,
9595
hostMode: HostMode.Development,
9696
corsOrigin: new List<string>() { "http://localhost:3000", "http://nolocalhost:80" },
97-
authenticationProvider: EasyAuthType.AppService.ToString(),
97+
authenticationProvider: "Unauthenticated",
9898
config: TEST_RUNTIME_CONFIG_FILE);
9999

100100
return ExecuteVerifyTest(options);
@@ -118,7 +118,7 @@ public Task CosmosDbNoSqlDatabase()
118118
setSessionContext: false,
119119
hostMode: HostMode.Production,
120120
corsOrigin: null,
121-
authenticationProvider: EasyAuthType.AppService.ToString(),
121+
authenticationProvider: "Unauthenticated",
122122
config: TEST_RUNTIME_CONFIG_FILE);
123123

124124
return ExecuteVerifyTest(options);
@@ -245,7 +245,7 @@ public Task TestSpecialCharactersInConnectionString()
245245
setSessionContext: false,
246246
hostMode: HostMode.Production,
247247
corsOrigin: null,
248-
authenticationProvider: EasyAuthType.AppService.ToString(),
248+
authenticationProvider: "Unauthenticated",
249249
config: TEST_RUNTIME_CONFIG_FILE);
250250

251251
return ExecuteVerifyTest(options);
@@ -301,6 +301,7 @@ public void EnsureFailureOnReInitializingExistingConfig()
301301
[DataRow("StaticWebApps", null, null, DisplayName = "StaticWebApps with no audience and no issuer specified.")]
302302
[DataRow("AppService", null, null, DisplayName = "AppService with no audience and no issuer specified.")]
303303
[DataRow("Simulator", null, null, DisplayName = "Simulator with no audience and no issuer specified.")]
304+
[DataRow("Unauthenticated", null, null, DisplayName = "Unauthenticated with no audience and no issuer specified.")]
304305
[DataRow("AzureAD", "aud-xxx", "issuer-xxx", DisplayName = "AzureAD with both audience and issuer specified.")]
305306
[DataRow("EntraID", "aud-xxx", "issuer-xxx", DisplayName = "EntraID with both audience and issuer specified.")]
306307
public Task EnsureCorrectConfigGenerationWithDifferentAuthenticationProviders(
@@ -384,7 +385,7 @@ public Task RestPathWithoutStartingSlashWillHaveItAdded()
384385
setSessionContext: false,
385386
hostMode: HostMode.Production,
386387
corsOrigin: null,
387-
authenticationProvider: EasyAuthType.AppService.ToString(),
388+
authenticationProvider: "Unauthenticated",
388389
restPath: "abc",
389390
config: TEST_RUNTIME_CONFIG_FILE);
390391

@@ -403,7 +404,7 @@ public Task GraphQLPathWithoutStartingSlashWillHaveItAdded()
403404
setSessionContext: false,
404405
hostMode: HostMode.Production,
405406
corsOrigin: null,
406-
authenticationProvider: EasyAuthType.AppService.ToString(),
407+
authenticationProvider: "Unauthenticated",
407408
graphQLPath: "abc",
408409
config: TEST_RUNTIME_CONFIG_FILE);
409410

@@ -466,7 +467,7 @@ public Task VerifyCorrectConfigGenerationWithMultipleMutationOptions(DatabaseTyp
466467
setSessionContext: true,
467468
hostMode: HostMode.Development,
468469
corsOrigin: new List<string>() { "http://localhost:3000", "http://nolocalhost:80" },
469-
authenticationProvider: EasyAuthType.AppService.ToString(),
470+
authenticationProvider: "Unauthenticated",
470471
restPath: "rest-api",
471472
config: TEST_RUNTIME_CONFIG_FILE,
472473
multipleCreateOperationEnabled: isMultipleCreateEnabled);
@@ -482,7 +483,7 @@ public Task VerifyCorrectConfigGenerationWithMultipleMutationOptions(DatabaseTyp
482483
setSessionContext: true,
483484
hostMode: HostMode.Development,
484485
corsOrigin: new List<string>() { "http://localhost:3000", "http://nolocalhost:80" },
485-
authenticationProvider: EasyAuthType.AppService.ToString(),
486+
authenticationProvider: "Unauthenticated",
486487
restPath: "rest-api",
487488
config: TEST_RUNTIME_CONFIG_FILE,
488489
multipleCreateOperationEnabled: isMultipleCreateEnabled);

src/Cli.Tests/ModuleInitializer.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ public static void Init()
8787
VerifierSettings.IgnoreMember<RuntimeConfig>(config => config.IsStaticWebAppsIdentityProvider);
8888
// Ignore the IsAppServiceIdentityProvider as that's unimportant from a test standpoint.
8989
VerifierSettings.IgnoreMember<RuntimeConfig>(config => config.IsAppServiceIdentityProvider);
90+
// Ignore the IsUnauthenticatedIdentityProvider as that's unimportant from a test standpoint.
91+
VerifierSettings.IgnoreMember<RuntimeConfig>(config => config.IsUnauthenticatedIdentityProvider);
9092
// Ignore the RestPath as that's unimportant from a test standpoint.
9193
VerifierSettings.IgnoreMember<RuntimeConfig>(config => config.RestPath);
9294
// Ignore the GraphQLPath as that's unimportant from a test standpoint.

0 commit comments

Comments
 (0)