When Azure SignalR Service is used with Functions, negotiating the protocol starts by getting the first connection information from Functions that contains both an access token and a URL to redirect. The client will use these to connect to the SignalR Service.
If that negotiate response includes tokenLifetimeSeconds, the client can ask the original hub for a new access token before the current one expires. The refresh request stays on the URI passed to HubConnection, and a returned accessToken replaces the token used for the service. See Authentication Refresh.
Azure SignalR Service with Azure Functions Authorization¶
To send x-ms-client-principal-id header for PlayFab and x-functions-key for Azure Functions key the default authenticator can be modified to append these headers in PrepareRequest:
usingSystem;usingBest.SignalR;namespaceBest.SignalR.Authentication{publicsealedclassAccessTokenAuthenticatorWithAzureFunctionAuthorization:IAuthenticationProvider{/// <summary>/// No pre-auth step required for this type of authentication/// </summary>publicboolIsPreAuthRequired{get{returnfalse;}}#pragma warning disable 0067/// <summary>/// Not used event as IsPreAuthRequired is false/// </summary>publiceventOnAuthenticationSuccededDelegateOnAuthenticationSucceded;/// <summary>/// Not used event as IsPreAuthRequired is false/// </summary>publiceventOnAuthenticationFailedDelegateOnAuthenticationFailed;#pragma warning restore 0067privateHubConnection_connection;privatestring_playFabId;privatestring_clientPrincipalId;publicAccessTokenAuthenticatorWithAzureFunctionAuthorization(HubConnectionconnection,stringplayFabId,stringclientPrincipalId){this._connection=connection;this._playFabId=playFabId;this._clientPrincipalId=clientPrincipalId;}/// <summary>/// Not used as IsPreAuthRequired is false/// </summary>publicvoidStartAuthentication(){}/// <summary>/// Prepares the request by adding two headers to it/// </summary>publicvoidPrepareRequest(Best.HTTP.HTTPRequestrequest){// Add Authorization header to http requests, add access_token param to the uri otherwiseif(Best.HTTP.Hosts.Connections.HTTPProtocolFactory.GetProtocolFromUri(request.CurrentUri)==Best.HTTP.Hosts.Connections.SupportedProtocols.HTTP){if(this._connection.NegotiationResult!=null)request.SetHeader("Authorization","Bearer "+this._connection.NegotiationResult.AccessToken);if(!string.IsNullOrEmpty(this._playFabId))request.SetHeader("x-ms-client-principal-id",this._playFabId);if(!string.IsNullOrEmpty(this._clientPrincipalId))request.SetHeader("x-functions-key",this._clientPrincipalId);}elseif(Best.HTTP.Hosts.Connections.HTTPProtocolFactory.GetProtocolFromUri(request.Uri)!=Best.HTTP.Hosts.Connections.SupportedProtocols.WebSocket)request.Uri=PrepareUriImpl(request.Uri);}publicUriPrepareUri(Uriuri){if(this._connection.NegotiationResult==null)returnuri;if(uri.Query.StartsWith("??")){UriBuilderbuilder=newUriBuilder(uri);builder.Query=builder.Query.Substring(2);returnbuilder.Uri;}if(Best.HTTP.Hosts.Connections.HTTPProtocolFactory.GetProtocolFromUri(uri)==Best.HTTP.Hosts.Connections.SupportedProtocols.WebSocket)uri=PrepareUriImpl(uri);returnuri;}privateUriPrepareUriImpl(Uriuri){if(this._connection.NegotiationResult!=null&&!string.IsNullOrEmpty(this._connection.NegotiationResult.AccessToken)){stringquery=string.IsNullOrEmpty(uri.Query)?"":uri.Query+"&";UriBuilderuriBuilder=newUriBuilder(uri.Scheme,uri.Host,uri.Port,uri.AbsolutePath,query+"access_token="+this._connection.NegotiationResult.AccessToken);returnuriBuilder.Uri;}returnuri;}publicvoidCancel(){}}}
varhub=newHubConnection(URL,protocol);hub.AuthenticationProvider=newAccessTokenAuthenticatorWithAzureFunctionAuthorization(hub,"<Play Fab ID>","<Azure Functions Key>");