SignalR and Web Api - How to modify the Hub class - asp.net-web-api

I am looking to notify clients of my Web Api that other clients have authenticated using SignalR. I understand the basics of SignalR but I am confused as to how to modify the Hub such that it can work with an API request.
The client is a Single Page Application which makes use of jQuery to call the Web Api. The client first authenticates with the API before it can use any of the API endpoints.
Also should add that I am authenticating with OAuth in the same project as the Web Api.

Well, the easiest way here is to get a reference to the signalR hub inside the oauth authentication flux, and then launch a global signaling after the successfull completion of this method: Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context).
For example:
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
// auth and other stuffs removed for brevity
// ...
// ...
context.Validated(ticket);
// then here
var hub = GlobalHost.ConnectionManager.GetHubContext<IMessageHub>("MessageHub");
hub.Clients.All.ServerMessage(new {message = $"{context.UserName} is here!"});
}
with a message hub like this:
public interface IMessageHub
{
void ServerMessage(object eventObject);
}
[HubName("MessageHub"), Authorize]
public class MessageHub : Hub<IMessageHub>
{
public MessageHub()
{
}
}
Hope it helps :)

Related

Slack Bolt SDK java Issue

Hi I am running Slack App which is written in the Bolt SDK For Java.
I am trying to do an OAuth By Bolt Java Code in Quarkus Framework . I am trying to call /slack/oauth/install as my redirect url where i can recieve the code . But however when i install the app its reporting that endpoint is not available (404)
Can someone help me why the endpoint is not visible and also tell me how to do OAuth Via Bolt SDK and then after getting access_token how can i redirect to my website log in page to connect the App .
#WebServlet("/slack/oauth")
public class OAuthController extends SlackAppServlet {
public OAuthController(){
super(initiateOauth());
}
public OAuthController(App app){
super(app);
}
private static App initiateOauth() {
AppConfig appConfig = AppConfig.builder().clientId(CLIENT_ID)
.clientSecret(CLIENT_SECRET)
.scope(CLIENT_SCOPE)
.oauthInstallPath("install")
.oauthRedirectUriPath("oauth_redirect")
.build();
App oauth = new App(appConfig);
oauth.asOAuthApp(true);
oauth.endpoint(WebEndpoint.Method.GET, "slack/oauth/install", (request, context) -> Response.ok(request.getRequestBodyAsString()));
return oauth;
}

Bearer token: The signature is invalid - Default ASP.NET Core 2.1 Web Api template published to Azure

I created a project in VS Community 2019 (latest update) with a template for WebApi .NET Core 2.1 and published it on Azure.
I only added a client secret in the app registration in the portal to use for the call using the authorization code flow.
I was trying to make a GET call using Postman with OAuth 2.0 authorization at the url below:
https://webapi3app.azurewebsites.net/api/values
But I get an unauthorized response with the error header below:
WWW-Authenticate:"Bearer error="invalid_token", error_description="The signature is invalid""
I tried decoding the client secret to BASE64 string but in the repsonse it says that it's an invalid client secret.
I tried changing authorization data to:
- Request URL.
- Request Headers.
I tried several grant types:
- Authorization code.
- Implicit.
- Password Credentials (after changing app to public client).
I tried several scopes:
- Default Microsoft scopes url (https://graph.microsoft.com/.default).
- user.read openid profile offline_access.
- https://aldolekalive.onmicrosoft.com/WebApi3/user_impersonation.
- profile openid email https://graph.microsoft.com/Directory.Read.All https://graph.microsoft.com/User.Read
I tried setting client authentication to:
- Send as basic auth header.
- Send client credentials in body.
I tried changing the Authorize attribute to authorize based on only AzureADBearer or only AzureADJwtBearer (because apparently by default they are both enabled with the current configuration) but no luck.
etc.
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.AzureAD.UI;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
.AddAzureADBearer(options => Configuration.Bind("AzureAd", options));
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseMvc();
}
}
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
[Authorize]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "value1", "value2" };
}
}
I expect to get a response with the body:
"value1, value2"
Per my understanding, your webapi is protected by Azure AD and now you want to call the api. To call the api you need to provide an access token.
To do this, you need to register two applications in Azure AD. One is for client App, the other one is for webapi. You can refer to my answer here.
And here is the complete sample. If you don't have an client application now, you can just register an client app in Azure portal, then use this client app to get an access token for your webapi.
I tried several scopes:
If you are using v2.0 endpoint, the scope should be api://{server_client_id}/.default.

Testing a secure spring mvc restful webservice

I have a secure web service that I have been trying to test. The service looks something like this:
#GetMapping(value= {"/v1/getSomeStuff"})
public SomeResp getSomeStuff(final UsernamePasswordAuthenticationToken token, #RequestParam(name="searchString", required=true) String request) {
//do some stuff in here
}
The services are secured using Spring Security and I am trying to write an IT test, that will supply the required UsernamePasswordAuthenticationToken. I have tried doing this:
#Test
#Sql(scripts = { "/db-scripts/company-script.sql","/db-scripts/user-script.sql" })
#WithUserDetails(value="testuser")
public void testGetSomeStuff() {
//test some stuff
getRestTemplate().getForEntity("http://localhost:" + port + "/v1/getSomeStuff?searchString=foo", SomeResp.class);
}
The Sql scripts are executing correctly and the User Details Service is being called, but the token is not being passed to the service. I understand (now) that #WithUserDetails is for test method security and won't do what I want. My question then is, is there another method for testing secured web services using the various spring test classes?

WebApi with OWIN SelfHost and Windows Authentication

I have a console application SERVER that hosts WebApi controllers using OWIN self-hosting, and runs under a custom account named "ServiceTest1".
In the same machine I have another console application CLIENT that runs under the account "ServiceTest2", and I want to capture in SERVER that "ServiceTest2" invoked a controller action. However:
WindowsIdentity.GetCurrent() is always "ServiceTest1".
Thread.CurrentPrincipal is an unauthenticated GenericIdentity.
RequestContext.Principal is null.
User is null.
What do I need to make this WebApi OWIN self-hosted to grab the Windows identity of the caller?
Your question is a little unclear on exactly how you've implemented the Windows authentication.
Enable Windows authentication:
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpListener listener = (HttpListener)app.Properties["System.Net.HttpListener"];
listener.AuthenticationSchemes = AuthenticationSchemes.IntegratedWindowsAuthentication;
// ...
}
}
Get the user in an OWIN middleware:
public async Task Invoke(IDictionary<string, object> env)
{
OwinContext context = new OwinContext(env);
WindowsPrincipal user = context.Request.User as WindowsPrincipal;
//...
}
Get the user in a Web API Controller:
// In a web api controller function
WindowsPrincipal user = RequestContext.Principal as WindowsPrincipal;

ASP.NET Web API session or something?

I need to store some information in session(or in whatever in ASP.NET Web API) that I need to retrieve in every API request. We will have one api IIS web site and multiple web site binding will be added through host header. When any request comes in for example, api.xyz.com, host header will be checked and store that website information in session that will be used in each subsequent api request when making a call to database.
I know there is no support for session in ASP.NET Web API. Is there any other way to handle this kind of situation? Where can I store information that can be retrieving in each subsequent request?
thanks.
in Global.asax add
public override void Init()
{
this.PostAuthenticateRequest += MvcApplication_PostAuthenticateRequest;
base.Init();
}
void MvcApplication_PostAuthenticateRequest(object sender, EventArgs e)
{
System.Web.HttpContext.Current.SetSessionStateBehavior(
SessionStateBehavior.Required);
}
give it a shot ;)
Well, REST by design is stateless. By adding session (or anything else of that kind) you are making it stateful and defeating any purpose of having a RESTful API.
The whole idea of RESTful service is that every resource is uniquely addressable using a universal syntax for use in hypermedia links and each HTTP request should carry enough information by itself for its recipient to process it to be in complete harmony with the stateless nature of HTTP".
So whatever you are trying to do with Web API here, should most likely be re-architectured if you wish to have a RESTful API.
With that said, if you are still willing to go down that route, there is a hacky way of adding session to Web API, and it's been posted by Imran here http://forums.asp.net/t/1780385.aspx/1
Code (though I wouldn't really recommend that):
public class MyHttpControllerHandler
: HttpControllerHandler, IRequiresSessionState
{
public MyHttpControllerHandler(RouteData routeData): base(routeData)
{ }
}
public class MyHttpControllerRouteHandler : HttpControllerRouteHandler
{
protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new MyHttpControllerHandler(requestContext.RouteData);
}
}
public class ValuesController : ApiController
{
public string GET(string input)
{
var session = HttpContext.Current.Session;
if (session != null)
{
if (session["Time"] == null)
{
session["Time"] = DateTime.Now;
}
return "Session Time: " + session["Time"] + input;
}
return "Session is not availabe" + input;
}
}
and then add the HttpControllerHandler to your API route:
route.RouteHandler = new MyHttpControllerRouteHandler();
In WebApi 2 you can add this to global.asax
protected void Application_PostAuthorizeRequest()
{
System.Web.HttpContext.Current.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Required);
}
Then you could access the session through:
HttpContext.Current.Session
You can use cookies if the data is small enough and does not present a security concern. The same HttpContext.Current based approach should work.
Request and response HTTP headers can also be used to pass information between service calls.
Now in 2017 with ASP.Net Core you can do it as explained here.
The Microsoft.AspNetCore.Session package provides middleware for managing session state.
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// Adds a default in-memory implementation of IDistributedCache.
services.AddDistributedMemoryCache();
services.AddSession(options =>
{
// Set a short timeout for easy testing.
options.IdleTimeout = TimeSpan.FromSeconds(10);
options.Cookie.HttpOnly = true;
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseSession();
}
From the Docs:
Introduction to session and application state in ASP.NET Core
Already tested on a working project

Resources