Skip to content

Support [AllowAnonymous] along with [Authorize] #1189

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -404,8 +404,8 @@ Both roles and policies are supported for output graph types, fields on output g
and query arguments. If multiple policies are specified, all must match; if multiple roles
are specified, any one role must match. You may also use `.Authorize()` and/or the
`[Authorize]` attribute to validate that the user has authenticated. You may also use
`.AllowAnonymous()` and/or `[AllowAnonymous]` to allow fields to be returned to
unauthenticated users within an graph that has an authorization requirement defined.
`.AllowAnonymous()` and/or `[AllowAnonymous]` to allow fields to bypass authorization
requirements defined on the type that contains the field.
Comment on lines +407 to +408
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a change, just a clarification.


Please note that authorization rules do not apply to values returned within introspection requests,
potentially leaking information about protected areas of the schema to unauthenticated users.
Expand Down
5 changes: 1 addition & 4 deletions src/Transports.AspNetCore/AuthorizationVisitorBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,7 @@ public virtual async ValueTask EnterAsync(ASTNode node, ValidationContext contex
_onlyAnonymousSelected.Push(ti);

// Fields, unlike types, are validated immediately.
if (!fieldAnonymousAllowed)
{
await ValidateAsync(field, node, context);
}
await ValidateAsync(field, node, context);
}

// prep for descendants, if any
Expand Down
31 changes: 31 additions & 0 deletions tests/Transports.AspNetCore.Tests/AuthorizationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -775,6 +775,37 @@ public async Task EndToEnd(bool authenticated)
actual.ShouldBe("""{"errors":[{"message":"Access denied for field \u0027parent\u0027 on type \u0027QueryType\u0027.","locations":[{"line":1,"column":3}],"extensions":{"code":"ACCESS_DENIED","codes":["ACCESS_DENIED"]}}]}""");
}

[Theory]
[InlineData("Role1", false, false)] // User with Role1, child requires Role2 - should fail at child level
[InlineData("Role2", false, false)] // User with Role2, query requires Role1 - should fail at query level
[InlineData("Role1,Role2", false, true)] // User with both roles - should pass
[InlineData(null, false, false)] // Unauthenticated user - should fail at query level
[InlineData("Role1", true, false)] // User with Role1, child requires Role2 and is anonymous - should fail
[InlineData("Role2", true, true)] // User with Role2, child requires Role2 and is anonymous - should pass
[InlineData("Role1,Role2", true, true)] // User with both roles, child is anonymous - should pass
[InlineData(null, true, false)] // Unauthenticated user, child is anonymous - should fail as Role2 is missing
public void BothAnonymousAndRequirements(string? userRoles, bool childIsAnonymous, bool expectedIsValid)
{
// Set up query to require Role1
_query.AuthorizeWithRoles("Role1");

// Set up child field to require Role2 and optionally be anonymous
_field.AuthorizeWithRoles("Role2");
if (childIsAnonymous)
_field.AllowAnonymous();

// Set up user principal based on test parameters
if (userRoles != null)
{
var roles = userRoles.Split(',');
var claims = roles.Select(role => new Claim(ClaimTypes.Role, role)).ToArray();
_principal = new ClaimsPrincipal(new ClaimsIdentity(claims, "Cookie"));
}

var ret = Validate(@"{ parent { child } }");
ret.IsValid.ShouldBe(expectedIsValid);
}

public enum Mode
{
None,
Expand Down
Loading