We are using the #microsoft/signalr JavaScript client in our Vue frontend to establish a websocket connection with our backend (.net core). Also, we use a Bearer token for authentication.
This is the connection builder in the frontend:
this.connection = new HubConnectionBuilder()
.withUrl('/chat', { accessTokenFactory: () => IdService.getToken() })
.configureLogging(LogLevel.Information)
.withAutomaticReconnect()
.build()
Then I start the connection in this code:
HubService.connection.start().then(() => {
console.log('Connection started')
}).catch(err => {
console.error(err)
})
My problem is, when the Bearer token expires in the backend, on a reconnect I get a 401 error in the frontend, which is correct and I want to respond correctly to this error. I can catch the error in the catch block of the start function but I don't know how to handle the error, because I can't read a status code from the request like in a normal HTTP request. Its just a error message from the signalr client. Of course I could search in the string for '401' but that seems wrong.
Error: Failed to start the connection: Error: Failed to complete negotiation with the server: Error: Unauthorized: Status code '401'
I would like to know where in my code and how to properly handle this type of error and other errors to. Any help or ideas are appreciated.
The correct way to to this is your IdService.getToken() get you a refreshed token when it is almost expires because it's said in the Microsoft documentation that this function is called every time that there is an communication from client to hub.
The access token function you provide is called before every HTTP request made by SignalR. If you need to renew the token in order to keep the connection active (because it may expire during the connection), do so from within this function and return the updated token.
There are many examples for the token providers to refresh the token when it is near expiration.
But if that fix/implementation is out of your reach/control, the only thing you can do is catch the exception and just initialize again the connection. Something like this:
Catch the exception;
Emit an event with 401 unauthorized exception.
Catch the event and just call the method that initializes your connection.
But this is a workaround to the original problem, that is the proper token refresh function.
Related
I have a login function which returns the user id and jwt token on successful login and different errors like username not found or incorrect password etc. if the data provided is incorrect. I tested the api using postman and it works fine but when i make a request from the front end to the login end point. It returns the credentials if the login attempt is valid but does not respond with and error if the login details are incorrect.
Login code
Currently im only logging the axios response to the console
Axios request
As per my comment above, you need to catch errors from your request.
axios.post("[your url here]")
.then(res=>{
console.log(res);
})
.catch(err=>{
console.log(err);
});
I follow instruction in openiddict server example using password flow from https://github.com/openiddict/openiddict-samples/tree/master/samples/PasswordFlow
but have no success.
It throws InvalidOperationException: An OpenID Connect response cannot be returned from this endpoint at route /connect/token:
return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
Postman params:
Content-Type: application/x-www-form-urlencoded
Params: username=..&password=...&grantType=password&scope=offline_access+profile+email
I spent my day for researching but there is no information about cannot be returned from this endpoint exception. And many people can run openiddict example except me.
Here is apart of Startup.cs:
services.AddEntityFrameworkSqlite()
.AddDbContext<MisapayContext>(options =>
{
options.UseOpenIddict<int>();
});
//....
services.AddOpenIddict<int>()
.AddEntityFrameworkCoreStores<MisapayContext>()
.DisableHttpsRequirement()
.EnableTokenEndpoint("/connect/token")
.EnableLogoutEndpoint("/connect/logout")
.EnableUserinfoEndpoint("/connect/userinfo")
.UseJsonWebTokens()
.AllowPasswordFlow()
.AllowRefreshTokenFlow()
.AddEphemeralSigningKey();
services.AddMvc(config =>
{
config.Filters.Add(new ApiExceptionFilter());
}).AddJsonOptions(options =>
{
options.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.Indented;
options.SerializerSettings.DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Local;
});
Edited: I think problem is OpenIdConnectRequest, it can not be binded if use:
OpenIddictBuiler.AddMvcBinders()
Will throws The OpenID Connect request cannot be retrieved from the ASP.NET context.`
Otherwise, remove it, OpenIdConnectRequest in AuthorizationController can get properly. And I can get request information such as username, password grantType etc... Strange... right?
Some other information:
Asp.net Core SDK 1.1
Project.json : https://gist.github.com/trinvh/47f29468887c209716098bc4c76181a7
Startup.cs: https://gist.github.com/trinvh/75b7a12fbee754d0ea8cf251f2da9fe9
AuthorizationController.cs: https://gist.github.com/trinvh/089015b2573cae550856631e72b81374
Any help will be appreciated!
Okay, here's what's happening:
You've configured OpenIddict to use /connect/token as the token endpoint address.
The token request you send via Postman points to /connect/token/, which is actually a totally different URL (/connect/token != /connect/token/).
Since the address differs from the registered endpoint path, OpenIddict doesn't handle the request and refuses to consider it as a token request.
For some reasons, MVC accepts to handle your /connect/token/ request and invokes the Exchange action, even though the route doesn't match the requested URL.
Since you haven't registered the OpenIddict MVC binder in the MVC options, MVC uses its default binder to construct the OpenIdConnectRequest object, which allows the OpenIdConnectRequest.GrantType parameter to be resolved from the invalid grantType parameter (it wouldn't happen with the dedicated OpenIddict binder).
Your token endpoint action ends up calling SignIn to return a token response.
Under the hood, OpenIddict detects that you called SignIn outside the normal token request processing - since it didn't consider the request as a token request, due to the paths difference - and aborts this unsafe operation by throwing an InvalidOperationException.
I'll ping the MVC folks to make sure they are aware of this bug.
Edit: after some research, it looks like this behavior is "by design" and was inherited from ASP.NET MVC. I opened a feature request in the aspnet/Mvc repository to add a new way to use "strict comparison" for routes matching.
I am getting 403 Forbidden Error from google API Javascript client. Following is my code:
gapi.load('client', function () {
console.log('gapi.client loaded.');
var discoveryUrl = 'https://sheets.googleapis.com/$discovery/rest?version=v4';
gapi.client.load(discoveryUrl).then(function () {
console.log('gapi.client.sheets loaded.');
gapi.client
.init({
apiKey: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
clientId: '0000000000000-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com',
scope: 'https://www.googleapis.com/auth/spreadsheets',
})
.then(function () {
return gapi.client.sheets.spreadsheets.get({
spreadsheetId: spreadsheetId,
});
})
.then(
function (response) {
console.log(response);
},
function (response) {
console.log(response);
},
);
});
});
My application is running in servlet container and oauth2 is handled at server side. If I want to add authToken how can I do it?
You may check in this documentation the reasons why you are getting a 403 Forbidden Error. It indicates that the server understood the request but refuses to authorize it. A server that wishes to make public why the request has been forbidden can describe that reason in the response payload (if any).
If authentication credentials were provided in the request, the server considers them insufficient to grant access. The client SHOULD NOT automatically repeat the request with the same credentials. The client MAY repeat the request with new or different credentials. However, a request might be forbidden for reasons unrelated to the credentials.
If I want to add authToken how can I do it?
Follow this documentation about Authorize Requests. You can identify your application using an OAuth 2.0 token.
You may also check on these related threads:
Getting 403 forbidden when using the Google Sheets API and a service account
Make sure that you grant the service account access to the file.
Getting a 403 - Forbidden for Google Service Account
You will get this error if you are emulating a device within Chrome DevTools.
If you try going through the usual "Sign in with Google" flow, you will see the following error:
Error 403: disallowed_useragent
You can’t sign in from this screen because this app doesn’t comply with
Google’s secure browsers policy. If this app has a website, you can open
a web browser and try signing in from there.
You can let the app developer know that this app doesn’t comply with
Google’s secure browsers policy.
See Google's documentation on this topic.
A workaround is to switch to device emulation only after going through the authorization flows.
We have a Web API 2 REST service. We're using the basic OWIN oAuth token authentication and we require https. We've added a RequireHttpsAttribute filter that checks and returns back a 403 HTTPS Required error when request is made with basic http.
The problem is our /token request. When we request our token we're NOT returning back the 403 error. In the Startup.Auth.cs config file we set AllowInsecureHttp=false. So this prevents users from requesting a token with an insecure call.
However, when this call is made we get a 404 Not Found error, not the 403 HTTPS Required that we want. Can anyone help me figure out how to fix this error?
I realize we're not using the RequireHttpsAttribute because this is happenign outside of the normal authentication, this is how we get the token FOR that authentication. So I'm not sure where I should be checking for the secure connection. I tried in the AuthenticationOAuthProviders class, in GrantResourceOwnersCredentials method. Before authenticating username and password I put in a check for https but I wasn't able to raise an HTTP Code error from there.
You could try using a simple Owin middle-ware at the beginning of your Owin pipeline, instead of a Web API filter. This way you'll catch every request made to your application.
Here is a small sample:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.Use(async (ctx, next) =>
{
if (ctx.Request.Scheme.Equals(Uri.UriSchemeHttps))
await next.Invoke();
else
ctx.Response.StatusCode = 403;
});
//other middlewares
//app.UseWebApi(..)
}
}
Attempting to sign in (and enter a session) using user credentials in an Angular app using the Backand SDK. From the Backand docs I am attempting to sign in using the Backand.signin() method (from my local) which looks to be initially sending an OPTIONS http request to the API which unfortunately is causing this cross origin error:
XMLHttpRequest cannot load https://api.backand.com/token. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:xxxx' is therefore not allowed access. The response had HTTP status code 400.
The exact response from the endpoint is: {"error":"unsupported_grant_type"}
I've combed through the documentation extensively but can't find anyone else having these errors.
This is exact code I am using:
function Login(username, password, callback) {
Backand.signin(username, password).then(function(response){
console.log(response);
}, function(error){
console.log(error);
});
}
The error is logged to the console as a null object.
It looks like the error was in fact on my end.
While attempting to set up my own Authorization service in my Angular app I inadvertently was adding an encoded Authorization token header somehow. When the requests were being made to Backand from the Backand SDK, the headers were not correctly set and thus causing issues.