I have created an Azure SignalR (Serverless) reosurce in azure portal.
Then I have created an azure function HttpTrigger locally that references Microsoft.Azure.WebJobs.Extensions.SignalRService. In my azure function I have this code:
`public static class HttpTrigger
{
[FunctionName("Negotiate")]
public static SignalRConnectionInfo Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
[SignalRConnectionInfo(HubName = "notificationHub")] SignalRConnectionInfo connectionInfo,
ILogger log)
{
log.LogInformation("Returning connection: " + connectionInfo.Url + "" + connectionInfo.AccessToken);
return connectionInfo;
}
[FunctionName("Notify")]
public static async Task<IActionResult> Notify([HttpTrigger(AuthorizationLevel.Function, "get", Route=null)] HttpRequest req,
[SignalR(HubName = "notificationHub")] IAsyncCollector<SignalRMessage> signalRMessage,
ILogger log)
{
log.LogInformation("Notify");
string msg = string.Format("Message from agent! {0} ", DateTime.Now);
await signalRMessage.AddAsync(
new SignalRMessage
{
Target = "notifications",
Arguments = new[] { msg }
});
return new OkObjectResult("ok");
}
}
`
Also in my azure function, this is what my local.settings.json looks like:
`
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"AzureSignalRConnectionString": "myconnstringhere"
},
"Host": {
"LocalHttpPort": 7071,
"CORS": "http://localhost:53377",
"CORSCredentials": true
}
}
To also solve the CORS problem, I have added http://localhost:53377 domain of my client part project.
My client part is a separate asp.net web application project . So here I am connecting to this azure function like this:
`
<script>
$(document).ready(function(){
const connection = new signalR.HubConnectionBuilder()
.withUrl("http://localhost:7071/api/")
.configureLogging(signalR.LogLevel.Information)
.build();
connection.onclose(start);
start(connection);
});
async function start(connection){
try {
await connection.start();
console.log("SignalR connected.");
connection.on("notifications", (message) => {
$("#detailcontainer").html(message);
console.log(message)
});
}
catch(err) {
console.log(err);
}
}
</script>
Now, I have published my azure function. But now it is not working anymore. It gets an error saying unauthorized when triggering /api/negotiate.
My azure function is a .net 6 project while the client app is a net framework 4.8. Is this because my client app is still in webforms?
I have added the connection string of my azure signalR to the application settings having a name format like this: Azure__SignalR__ConnectionString
I also have configured CORS allowed origins for my azure function, I added my client localhost app.
Closing this one because I found the answer. And it was really annoying that I have missed this one out.
I replaced AuthorizationLevel.Function to AuthorizationLevel.Anonymous. Because I am just passing the domain/api of my azure function and letting the signalR do its thing on their JS.
Related
I was initially trying to setup my server-side signalR HUB to send messages to the client, but so far have not succeeded.
So I decided to try to send a message from the client instead; and setup a button to trigger a message from client to server.
I can launch my Core 3.1 project (image below), and setup the Hub connection just fine, but cannot verify in any way that the message is being received on the server.
In fact, my server breakpoints never get hit.
In my html:
<button mat-button (click)="sendClientMessage()"> Send Message </button>
TypeScript component:
sendClientMessage(): void {
this.notificationService.sendMessageToHub();
}
import { Injectable } from '#angular/core';
import * as signalr from '#microsoft/signalr';
import { SIGCONT } from 'constants';
#Injectable({
providedIn: 'root',
})
export class NotificationService {
private hubConnection: signalr.HubConnection;
hubMessage: string;
public startConnection = () => {
this.hubConnection = new signalr.HubConnectionBuilder()
.withUrl('https://localhost:44311/hub')
.configureLogging(signalr.LogLevel.Debug)
.build();
this.hubConnection
.start()
.then(() => {
console.log('Hub Connection started');
this.sendMessageToHub();
})
.catch((err) => console.log(`Error while starting connection: ${err}`));
this.hubConnection.serverTimeoutInMilliseconds = 50000;
}
public hubListener = () => {
this.hubConnection.on('messageReceived', (message) => {
this.hubMessage = message;
console.log(message);
});
}
public sendMessageToHub = () => {
if (this.hubConnection == undefined || this.hubConnection.state === signalr.HubConnectionState.Disconnected) {
this.startConnection();
} else {
this.hubConnection.send('NewMessage', 'client', 'You have a notification from the front end !')
.then(() => console.log('Message sent from client.'));
}
}
constructor() { }
}
My server-side Core project - Notifications.cs
using Microsoft.AspNetCore.SignalR;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace NotificationHub.Hubs
{
public class Notifications: Hub
{
public async Task NewMessage(long username, string message)
{
await Clients.All.SendAsync("messageReceived", username, message);
}
internal Task NewMessage(string v1, string v2)
{
throw new NotImplementedException();
}
}
}
When I click the button above, it appears to send something to the server:
I would appreciate any help in getting my Core project to first hit those breakpoints, and to see what the NewMessage method is not receiving the client message.
From there I can try and figure out how to send messages from server to client (i.e. using some Timer example).
thank you.
You're sending 2 strings from the client to the server but you've made your server method take a long and a string so it doesn't match. If you looked at the server logs you would see a message about the method no being found.
Another way to observe the error would be to call invoke instead of send from the client side which will expect a response from the server on completion of the hub method, or in this case an error will be sent from the server.
I am using React as client and Web API core for back end interaction.
For Authentication we are using Token based authentication using AspNet.Security.OpenIdConnect.Server (ASOS).
I have to implement refresh token scenario where on expiration of access token we use refresh token (returned by ASOS) to get new access Token.
I know one way to achieve by calling method on client is in AXIOS interceptor like below.
httpPromise.interceptors.response.use(undefined, err => {
const { config, response: { status } } = err;
const originalRequest = config;
if (status === 401) {
var refresh_Token = JSON.parse(window.localStorage.getItem('refreshToken'));
fetch(globalConstant.WEB_API_BASE_PATH + "authtoken,
{
method: "POST",
headers: new Headers({
'Content-Type': 'application/json',
})
},
data:{grant-type:"refresh_Token",refresh_token:"refresh Token ....."
)
....other logic to set new access token and make call again to existing
request.
}
})
I want to done it in WEB API Core side, so that in middle ware or in authentication pipeline it detects access token expiration and return new access token. The glimpse of WEB API code is like below.
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
.... some code
serives.AddAuthentication(o =>
{
o.DefaultAuthenticateScheme = OAuthValidationDefaults.AuthenticationScheme;
})
serives.AddOAuthValidation()
serives.AddOpenIdConnectServer(options =>
{
options.ProviderType = typeof(AuthorizationProvider);
options.Provider = new AuthorizationProvider(new SecurityService());
options.TokenEndpointPath = "/authtoken";
options.UserinfoEndpointPath = "/userInfo";
options.AllowInsecureHttp = true;
options.ApplicationCanDisplayErrors = true;
});
..some code
}
The links i followed How to handle expired access token in asp.net core using refresh token with OpenId Connect and https://github.com/mderriey/aspnet-core-token-renewal/blob/master/src/MvcClient/Startup.cs
How to do authentication via custom token server in Meteor.js?
Is there any package like accounts-google for custom token server which handles authentication by just taking token endpoints, client id, secrete, and scope as configuration parameter.
I don't know of a generic oauth package. But it shouldn't be too difficult to write a package for your particular server, as there are a number of examples to look at.
Using accounts-github as an example, here's the code for making the connection on the client. Note the endpoint URL, client id, scope, etc. This will handle the popup for you, but you'll probably want to include custom CSS:
var loginUrl =
'https://github.com/login/oauth/authorize' +
'?client_id=' + config.clientId +
'&scope=' + flatScope +
'&redirect_uri=' + OAuth._redirectUri('github', config) +
'&state=' + OAuth._stateParam(loginStyle, credentialToken);
OAuth.launchLogin({
loginService: "github",
loginStyle: loginStyle,
loginUrl: loginUrl,
credentialRequestCompleteCallback: credentialRequestCompleteCallback,
credentialToken: credentialToken,
popupOptions: {width: 900, height: 450}
});
And here's a snippet from the server side, completing the process to get an access token:
var getAccessToken = function (query) {
var config = ServiceConfiguration.configurations.findOne({service: 'github'});
if (!config)
throw new ServiceConfiguration.ConfigError();
var response;
try {
response = HTTP.post(
"https://github.com/login/oauth/access_token", {
headers: {
Accept: 'application/json',
"User-Agent": userAgent
},
params: {
code: query.code,
client_id: config.clientId,
client_secret: OAuth.openSecret(config.secret),
redirect_uri: OAuth._redirectUri('github', config),
state: query.state
}
});
} catch (err) {
throw _.extend(new Error("Failed to complete OAuth handshake with Github. " + err.message),
{response: err.response});
}
if (response.data.error) { // if the http response was a json object with an error attribute
throw new Error("Failed to complete OAuth handshake with GitHub. " + response.data.error);
} else {
return response.data.access_token;
}
};
And utilizing the token to get the user identity:
var getIdentity = function (accessToken) {
try {
return HTTP.get(
"https://api.github.com/user", {
headers: {"User-Agent": userAgent}, // http://developer.github.com/v3/#user-agent-required
params: {access_token: accessToken}
}).data;
} catch (err) {
throw _.extend(new Error("Failed to fetch identity from Github. " + err.message),
{response: err.response});
}
};
The github and the accounts-github packages should be very helpful as references.
I have a running multichannelapplication connceted via wcf service to a sqlserver 2012. When I stopp the service the app keep running and the data is stored in the entity manager of breeze:
(function () {
var oldClient = OData.defaultHttpClient;
var myClient = {
request: function (request, success, error) {
if (request.requestUri.indexOf("$metadata", request.requestUri.length - "$metadata".length) !== -1) {
request.headers.Accept = "application/xml";
}
return oldClient.request(request, success, error);
}
};
OData.defaultHttpClient = myClient;
breeze.config.initializeAdapterInstance("dataService", "OData", false);
var dataNS = DevExpress.data;
var manager = new breeze.EntityManager({
dataService: new breeze.DataService({
serviceName: "http://localhost:57049/DataService.svc",
hasServerMetadata: false,
adapterName: "OData"
})
});
App.db = {
tblInvoice: new dataNS.BreezeStore({
entityManager: manager,
resourceName: "tblInvoice",
autoCommit: true,
}),
when i restart the service the data should be synchronized but it doesn't do this automaticly. The Breeze api says saveChanges() to save to entity manager. How to sync the entities with the server if the service is available again?
I've got a wcf Service which has a method which send its output in jsonformat. The service is hosted in an https-Environment.
i'm calling it with angularjs-resource:
var hrdemo = angular.module('hrdemo', ["ngResource"]);
hrdemo.controller('HrDemoCtrl', function ($scope, hrdbservice) {
$scope.items = hrdbservice.get({ 'Id': 1 });
var a = $scope.items.length;
});
hrdemo.factory('hrdbservice', function ($resource) {
return $resource('http://hrservice/HrService.svc/:Id', { Id: "#Id" }, { get: { method: 'JSONP' } });
});
Angularjs runs in an ASP.Net-Web Application.
When calling the Service I get something like an xhr-problem.
1) How can i authenticate with my Windows authentication over angularjs
2) What can I do to fix the xhr-problem?
May the withCredentials option is what you are searching for $http.post(url, {withCredentials: true, ...}). Also there is a specific shortcut for performing a JSONP request in AngularJS - $http.jsonp()
AngularJS Docs