Protoc-only generated Service Client and API Key Auth

Here is my situation:

SS API A:

1-Still at v6.5.0 and stuck on .Net Framework 4.7.2
2-Uses protoc-only generated Service Client to call SS API B.
3-Uses interceptor to use set Bearer with API Key provided by SS API B.
4-Doesn’t use Client Certificate.
5-Calls SS API B endpoints on secured https 5051

SS API B:

1-.Net 8 Kestrel windows service listening on secured https gRPC 5051 and Https 5001
2-Uses gRPC, ASP.Net Core IdentityAuth and API Keys features.
3-Services must be protected so client must be authenticated and have Admin role.

Everything works great except when I want to setup the authentication/authorization.
I have to admit that I am very confused on what I have to do on the ASP.NET core side vs SS when it comes to do the authentication and authorization.

Currently, using the API Explorer (/ui) and the default users or the standalone API key I created for API A, it works fine from the UI. But when calls are made using the same API Key from my gRPC Client on API A, it doesn’t. API A has the correct Authentication Bearer header API key on each request but how API B is going to create the custom SS session from it? Should I add services.AddAuthentication().something?

Is this something with the service attributes ( I have tried a bunch of combinations), should those be on the requests instead?

Any help would be much appreciated.

API A :

private readonly Lazy<GrpcServices.GrpcServicesClient> _client = new(() => new GrpcServices.GrpcServicesClient(
        GrpcChannel.ForAddress($"https://{Connection.HostName}:{Connection.HostPort}", new GrpcChannelOptions {
            HttpClient = new System.Net.Http.HttpClient(new WinHttpHandler() {              
                ServerCertificateValidationCallback = (req, cert, certChain, sslErrors) =>
                    cert.SubjectName.RawData.SequenceEqual(cert.IssuerName.RawData) && // self-signed
                    cert.GetNameInfo(X509NameType.DnsName, forIssuer:false) == Connection.HostName &&
                    (sslErrors & ~SslPolicyErrors.RemoteCertificateChainErrors) == SslPolicyErrors.None // only this
            }),
            Credentials = ChannelCredentials.Create( new SslCredentials(), GetCredentials())
        })));
    private static CallCredentials GetCredentials()
    {
        var credentials = CallCredentials.FromInterceptor(async (context, metadata) =>
        {
            metadata.Add("Authorization", $"Bearer {Connection.ApiKey}");
        });
        
        return credentials;
    }


Client gets error :

Status(StatusCode=“Unauthenticated”, Detail=“Unauthorized”)

when calling the endpoint

API B:

builder.Services.AddAuthorization();
builder.Services.AddIdentity<ApplicationUser, IdentityRole>(options => {
        options.SignIn.RequireConfirmedAccount = true;
    })
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddSignInManager()
    .AddDefaultTokenProviders();

builder.Services.ConfigureApplicationCookie(options => options.DisableRedirectsForApis());
builder.Services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, AdditionalUserClaimsPrincipalFactory>();
...
[Authenticate]
[ValidateIsAuthenticated]
[RequiredRole("Admin")]
public class UAService: Service{}


Inside the service request handler, the session is NULL:

public async Task<UAWriteResponse> Any(UAWriteMultiValuesRequest request)
{
var session = await GetSessionAsync() as CustomUserSession;
..
}

When dumping the base.Request:

Items: 
	{
		__AuthorizationMiddlewareWithEndpointInvoked: {},
		__apikey: 
		{
			__type: "ServiceStack.ApiKeysFeature+ApiKey, ServiceStack.Server",
			id: 3,
			key: full key displayed correctly here,
			name: test,
			visibleKey: ak-***72b,
			createdDate: 2024-11-07T17:40:31.0111292-05:00,
			lastUsedDate: 2024-11-13T16:16:39.8563099Z,
			scopes: 
			[
				Admin
			],
			features: [],
			restrictTo: [],
			notes: test notes
		},
		__session: 
		{
			__type: "ServiceStack.AuthUserSession, ServiceStack",
			id: 361bb9244b5044479b6a5408a96636b4,
			userAuthId: 0,
			userAuthName: authsecret,
			userName: authsecret,
			displayName: Admin,
			createdAt: 0001-01-01,
			lastModified: 0001-01-01,
			roles: 
			[
				Admin
			],
			permissions: [],
			isAuthenticated: True,
			fromToken: False,
			tag: 0,
			authProvider: authsecret,
			providerOAuthAccess: [],
			meta: {}
		}

The Identity Auth API Keys feature is completely separate from Authentication, and cannot be used to Authenticate a user. They’re used for calling APIs protected by [ValidateApiKey], i.e. as distinct from calling Authentication protected APIs using [ValidateIsAuthenticated] or [Authenticate] attributes.

If your requirement is for clients to be Authenticated they shouldn’t use the newest API Keys feature.

Thanks Mythz. I am ok having just a couple of endpoints with the [ValidateApiKey] and the rest of my services using Auth and users for other ones. I tested it and it works fine. My client is only able to execute the requests if is has a valid API Key.

One issue I see now is with the Restrict To APIs.
I have 4 requests with the [ValidateApiKey] and they are all listed. I choose one on the list to restrict my API key to only this one but my client is able to use all 4…
I can see the API has the correct restrictTo on the __apikey:

restrictTo: 
			[
				UAReadSingleNodeValueRequest
			],

Any idea why?

I found an issue after a valid API Key was used and cached it wasn’t re-validated per API Request after resolving from the cached valid keys which is now resolved. Can you try with the latest v8.4.1+ that’s now available in pre-release packages.

Thanks, will try but I see a publish date of November 18 on the pre-release site and November 19 on MyGet.org… are they the same?

The last pre-release package was published to MyGet 8 hours ago, just after the last commit:

The same version was published to all our pre-release package sources:

I still see the same behaviour with the pre-release version.

I can’t reproduce the issue, will need a stand-alone repro (e.g. on GitHub) to be able to repro and resolve the issue.

It’s never fun to hear that one :slight_smile: …I will get on it whenever I can and post back here when ready. Thanks.