Azure / azure-signalr

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

Support extracting userId from incoming JWT claims #2056

Open yohny opened 1 week ago

yohny commented 1 week ago

Currently there are limited ways to specify user identity to be used for SignalR in Azure functions when using Node.js runtime. One can use input binding property userId to only specify that it should be extracted from header field or query parameter. However these days the APIs are commonly called with JWT tokens, that already contain verified user identifier and this is also common case when calling SignaR negotiate endpoint. It would be helpful to extend userId to support also specifying the claim name from incoming JWT token to be used as user identifier for SIgnalR, for example:

const inputSignalR = input.generic({
  type: "signalRConnectionInfo",
  name: "connectionInfo",
  hubName: "my-hub",
  userId: "{claim.account_id}", // allow to specify claim name
  idToken: "{headers.authorization}", // extract JWT from Authorization header, already possible, but  issue #2055
  claimTypeList: ["name", "email", "account_id"], // propagate claims to SignalR access token, already possible, but not good enough
});

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],
});

With combination of idToken and claimTypeList it is already possible to propagate claims from incoming JWT token into SignalR access token, but there is no way to specify which one is supposed to be used as user idemtity, therefore SignalR does not pick it up, even if identiy claim is propagated. Thats where the enhancement of userId possibilities would help - you could explicitly specify which claim is to be used as user identity for SignalR. This would allow to solve the use cases like this

vicancy commented 2 days ago

Sounds nice, cc @Y-Sindo

Y-Sindo commented 2 days ago

For all language frameworks, App Service makes the claims in the incoming token (whether from an authenticated end user or a client application) available to your code by injecting them into the request headers.

You can use userId: {headers.X-MS-CLIENT-PRINCIPAL-NAME} to get the principle name, and {headers.X-MS-CLIENT-PRINCIPAL-ID} to get the principle ID. Is that enough for you?

yohny commented 2 days 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 email claim, than it does not help. We need the claim account_id to be used as the user identifier for SignalR.

Y-Sindo commented 1 day ago

Currently the header can't be customized to be account_id. We will support this feature in SignalR extensions.