Azure / azure-signalr

Azure SignalR Service SDK for .NET
https://aka.ms/signalr-service
MIT License
424 stars 100 forks source link

SignalR input binding fails to parse standard Authorization header #2055

Open yohny opened 1 week ago

yohny commented 1 week ago

When using the idToken property on SignalR input binding as specified in the documentation it should be possible to use binding expession to specify the header from which the JWT token should be extracted. Then using claimTypeList you can specify which extracted claims should be propagated to resulting access token for SignalR. However if standard Authorization header is specified in idToken, the JWT token parsing fails. Sample Azure function (nodejs runtime, programing model v4):

const inputSignalR = input.generic({
  type: "signalRConnectionInfo",
  name: "connectionInfo",
  hubName: "my-hub",
  idToken: "{headers.authorization}", // extract JWT from standard Authorization header with Bearer JWT token in it
  claimTypeList: ["name", "email"], // propagate name and email claims to SignalR Service access token
});

async function negotiate(
  request: HttpRequest,
  context: InvocationContext
): Promise<HttpResponseInit> {
    return { body: JSON.stringify(context.extraInputs.get(inputSignalR)) };
}

app.post("negotiate", {
  authLevel: "anonymous",
  handler: negotiate,
  route: "negotiate",
  extraInputs: [inputSignalR],
});

when such negotiate endpoint is invoked, it however fails with error:

Executed 'Functions.negotiate' (Failed, Id=8c21bcd7-d061-4184-bac7-3f1046b00f99, Duration=71ms)
[2024-09-29T20:21:11.648Z] System.Private.CoreLib: Exception while executing function: Functions.negotiate. System.IdentityModel.Tokens.Jwt: IDX12709: CanReadToken() returned false. JWT is not well formed.
The token needs to be in JWS or JWE Compact Serialization Format. (JWS): 'EncodedHeader.EndcodedPayload.EncodedSignature'. (JWE): 'EncodedProtectedHeader.EncodedEncryptedKey.EncodedInitializationVector.EncodedCiphertext.EncodedAuthenticationTag'.

this can be resolved by removing the Bearer string from the begginning of the incoming Authorizzation header. If this is done, the JWT header is parsed successfully and the specified claims are propagated to accessToken returned by negotiate endpoint. It would be helpful if parsing of JWT token could handle common Bearer prefix in the Authorization header without failure.

Further technical details

Nodejs 20.15..1 @azure/functions 4.5.1

Y-Sindo commented 1 day ago

Are you using a specific identity provider, the provider tokens are injected into the request headers. See https://learn.microsoft.com/azure/app-service/configure-authentication-oauth-tokens#retrieve-tokens-in-app-code for more information. Does it satisfy your need?

yohny commented 1 day ago

We use a third party Identity Provider and in JWT token issued by it, the user identifier claim is named account_id, but there are of course also other more standard claims present there like email and name . Which claims will be propagated into those automatically populated headers (X-MS-CLIENT-PRINCIPAL-ID, X-MS-CLIENT-PRINCIPAL-NAME)? Or is it somehow configurable? Because if its name or enail, that does not help. We need the claim account_id to be used as the user identifier for SignalR.

I actually created a future request https://github.com/Azure/azure-signalr/issues/2056 to allow to explicitly specify the claim that should be used as user identifier for SignalR, for which the ability to parse the standard Bearer Authorization header is a prerequisite (as normally the Bearer prefix is present there).