moozzyk / SignalR-Client-Swift

Swift SignalR Client for Asp.Net Core SignalR server
MIT License
351 stars 132 forks source link

403 or May be CORS issue. #161

Closed govi2010 closed 3 years ago

govi2010 commented 3 years ago

Before filing an issue Check Frequently Asked Questions

Describe the bug

Below are server logs. Somehow I can getting 403 error there is not authorisation On server. JAVA client works well without authorisation.

It seems like CORS issue but I don't know what origin do i need to add on server side.

2020-12-19 15:01:42.010 +00:00 [Debug] Microsoft.AspNetCore.Routing.Matching.DfaMatcher: 1 candidate(s) found for the request path '/SignalHub/negotiate'
2020-12-19 15:01:42.010 +00:00 [Debug] Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware: Request matched endpoint '/SignalHub/negotiate'
2020-12-19 15:01:42.010 +00:00 [Information] Microsoft.AspNetCore.Routing.EndpointMiddleware: Executing endpoint '/SignalHub/negotiate'
2020-12-19 15:01:42.010 +00:00 [Debug] Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionManager: New connection 5wxZU1GBRGTf_3WeTfalUQ created.
2020-12-19 15:01:42.010 +00:00 [Debug] Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionDispatcher: Sending negotiation response.
2020-12-19 15:01:42.010 +00:00 [Information] Microsoft.AspNetCore.Routing.EndpointMiddleware: Executed endpoint '/SignalHub/negotiate'
2020-12-19 15:01:42.010 +00:00 [Information] Microsoft.AspNetCore.Hosting.Diagnostics: Request finished in 0.3834ms 200 application/json

2020-12-19 15:01:43.664 +00:00 [Information] Microsoft.AspNetCore.Hosting.Diagnostics: Request starting HTTP/1.1 GET https://stage.blababla.com/SignalHub?connectiontype=host&connectionkey=d5be20cd-fcf8-43fc-aa81-d349de9546b1&id=cre9lPGlk7wLjv82N_nXjQ
2020-12-19 15:01:43.664 +00:00 [Trace] Microsoft.AspNetCore.HostFiltering.HostFilteringMiddleware: All hosts are allowed.
2020-12-19 15:01:43.664 +00:00 [Debug] Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler: AuthenticationScheme: Bearer was not authenticated.
2020-12-19 15:01:43.664 +00:00 [Debug] Microsoft.AspNetCore.Cors.Infrastructure.CorsService: The request has an origin header: 'wss://stage.blababla.com/SignalHub?connectiontype=host&connectionkey=d5be20cd-fcf8-43fc-aa81-d349de9546b1&id=cre9lPGlk7wLjv82N_nXjQ'.
2020-12-19 15:01:43.664 +00:00 [Information] Microsoft.AspNetCore.Cors.Infrastructure.CorsService: CORS policy execution failed.
2020-12-19 15:01:43.664 +00:00 [Information] Microsoft.AspNetCore.Cors.Infrastructure.CorsService: Request origin wss://stage.blababla.com/SignalHub?connectiontype=host&connectionkey=d5be20cd-fcf8-43fc-aa81-d349de9546b1&id=cre9lPGlk7wLjv82N_nXjQ does not have permission to access the resource.
2020-12-19 15:01:43.664 +00:00 [Debug] Microsoft.AspNetCore.WebSockets.WebSocketMiddleware: Request origin wss://stage.blababla.com/SignalHub?connectiontype=host&connectionkey=d5be20cd-fcf8-43fc-aa81-d349de9546b1&id=cre9lPGlk7wLjv82N_nXjQ is not in the list of allowed origins.
2020-12-19 15:01:43.664 +00:00 [Information] Microsoft.AspNetCore.Hosting.Diagnostics: Request finished in 0.4212ms 403
2020-12-19 15:01:57.612 +00:00 [Trace] Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionManager: Connection 5wxZU1GBRGTf_3WeTfalUQ timed out.
2020-12-19 15:01:57.612 +00:00 [Trace] Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionContext: Disposing connection 5wxZU1GBRGTf_3WeTfalUQ.
2020-12-19 15:01:57.612 +00:00 [Trace] Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionContext: Waiting for None transport to complete.
2020-12-19 15:01:57.612 +00:00 [Trace] Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionContext: None transport complete.
2020-12-19 15:01:57.612 +00:00 [Debug] Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionManager: Removing connection cre9lPGlk7wLjv82N_nXjQ from the list of connections.
2020-12-19T15:03:05  No new trace in the past 1 min(s).
2020-12-19T15:04:05  No new trace in the past 2 min(s).
2020-12-19T15:05:05  No new trace in the past 3 min(s).
2020-12-19 15:05:44.843 +00:00 [Information] Microsoft.AspNetCore.Hosting.Diagnostics: Request starting HTTP/1.1 GET http://stage.blababla.com/
2020-12-19 15:05:44.843 +00:00 [Trace] Microsoft.AspNetCore.HostFiltering.HostFilteringMiddleware: All hosts are allowed.
2020-12-19 15:05:44.844 +00:00 [Debug] Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler: AuthenticationScheme: Bearer was not authenticated.
2020-12-19 15:05:44.844 +00:00 [Debug] Microsoft.AspNetCore.HttpsPolicy.HttpsRedirectionMiddleware: Redirecting to 'https://stage.blababla.com/'.
2020-12-19 15:05:44.845 +00:00 [Information] Microsoft.AspNetCore.Hosting.Diagnostics: Request finished in 1.5602ms 307
2020-12-19T15:07:05  No new trace in the past 1 min(s).

Expected behavior Connection ID is issued by server. but after that connectionDidFailToOpen method called instead of connectionDidOpen.

Code To Reproduce the Issue IOS Code is below. I am passing two query param.

//
//  SignalRClient.h
//  Followme
//
//  Created by Subo on 2018/12/25.
//  Copyright © 2018年 com.followme. All rights reserved.
//

import Foundation
import SwiftSignalRClient

@objc public protocol SignalRClientDelegate : class {
    func clientDidConnectSuccess(client: SignalRClient)
    func clientDidConnectFailed(client: SignalRClient, error: Error)
    func clientDidClosed(client: SignalRClient, error: Error?)
}

/// 因为SwiftSignalRClient没有做OC的支持,所以这里多包装了一层来支持OC
@objcMembers public class SignalRClient : NSObject {
    var url: String
    var headers: [String: String]?

    private var connection: HubConnection?
    public weak var delegate: SignalRClientDelegate?

    public init(url: String, headers: [String: String]?) {
        self.url = url
        self.headers = headers
        super.init()
    }

    public func start() {
        self.connection = HubConnectionBuilder(url: URL(string: self.url)!)
            .withLogging(minLogLevel: .debug)
            .withHttpConnectionOptions(configureHttpOptions: { (httpConnectionOptions) in
                if let header = self.headers {
                    for (key, value) in header {
                        httpConnectionOptions.headers[key] = value
                    }
                }
            })
            .build()
        self.connection?.delegate = self
        self.connection?.start()
    }

    public func stop() {
        self.connection?.stop()
    }

    public func on(method: String, callback: @escaping (_ jsonString: String?) -> Void) {
        self.connection?.on(method: method, callback: {(args) in
            callback(args)
        })
    }

    public func send(method: String, arguments:[AnyObject], sendDidComplete: @escaping (_ error: Error?) -> Void) {
        self.connection?.send(method: method, arguments:  arguments as! [Encodable], sendDidComplete: sendDidComplete)
    }

    public func invoke(method: String, arguments: [AnyObject], invocationDidComplete: @escaping (_ error: Error?) -> Void) {
        self.connection?.invoke(method: method, arguments: arguments as! [Encodable], invocationDidComplete: invocationDidComplete)
    }
}

extension SignalRClient: HubConnectionDelegate {

    public func connectionDidOpen(hubConnection: HubConnection) {
        self.delegate?.clientDidConnectSuccess(client: self)
    }

    public func connectionDidFailToOpen(error: Error) {
        self.delegate?.clientDidConnectFailed(client: self, error: error)
    }

    public func connectionDidClose(error: Error?) {
        self.delegate?.clientDidClosed(client: self, error: error)
    }
}

Additional context Add any other context about the problem here.

moozzyk commented 3 years ago

I see two issues in the logs - one seems related to CORS and one seems related to token authentication. Also, I don't know how is your CORS policy configured. I would recommend:

govi2010 commented 3 years ago

@moozzyk

I don't have any authentication on my hub.

//// No authorize 
    public class SignalHub : Hub
    {
    }

in my startup.cs file in public void ConfigureServices(IServiceCollection services) method I have

                services.AddCors(options => options.AddPolicy("Cors", builder =>
                {
                    builder
                        .WithOrigins(
                                    "http://localhost:4200",
                                    "https://stage.blabla.com",
                                    "http://stage.blabla.com")
                        .AllowAnyMethod()
                        .AllowAnyHeader()
                        .AllowCredentials();
                }));
                /// this is for only API not for SignalR
                var signingKey =
                    new SymmetricSecurityKey(
                        Encoding.UTF8.GetBytes(Configuration[Constants.ConfigurationKeys.Token.SigningKey]));
                services.AddAuthentication(options =>
                {
                    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                }).AddJwtBearer(config =>
                {
                    config.RequireHttpsMetadata = false;
                    config.SaveToken = true;
                    config.TokenValidationParameters = new TokenValidationParameters
                    {
                        IssuerSigningKey = signingKey,
                        ValidateAudience = true,
                        ValidAudience = Configuration[Constants.ConfigurationKeys.Token.Audience],
                        ValidateIssuer = true,
                        ValidIssuer = Configuration[Constants.ConfigurationKeys.Token.Issuer],
                        ValidateLifetime = true,
                        ValidateIssuerSigningKey = true
                    };
                    config.Events = new JwtBearerEvents
                    {
                        OnAuthenticationFailed = context =>
                        {
                            if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                            {
                                context.Response.Headers.Add("Token-Expired", "true");
                            }
                            return Task.CompletedTask;
                        },
                        OnTokenValidated = async context =>
                                                  {
                                                   var userService = context.HttpContext.RequestServices.GetRequiredService<UserManager<ApplicationUser>>();
                                                  var session = context.HttpContext.RequestServices.GetRequiredService<SessionManager>();
                                                  var claimsIdentity = context.Principal;
                                                  var email = claimsIdentity.Identity.Name;
                                                  }
                    };
                });

               services.AddAuthentication();

in my startup.cs file in public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILogger<Startup> logger) method I have

                app.UseAuthentication(); /// this authentication is for API controller but not for signalrRhub
                app.UseCors("Cors");
                var webSocketOptions = new WebSocketOptions()
                {
                    KeepAliveInterval = TimeSpan.FromSeconds(120),
                    ReceiveBufferSize = 4 * 1024
                };
                webSocketOptions.AllowedOrigins.Add("http://localhost:4200");
                webSocketOptions.AllowedOrigins.Add("https://stage.blabla.com");
                webSocketOptions.AllowedOrigins.Add("http://stage.blabla.com");
                app.UseWebSockets(webSocketOptions);
                app.UseCors("Cors");
govi2010 commented 3 years ago

@moozzyk I wonder why this same hub is working fine with JAVA client and JS client without authorization and without CORS issue,

moozzyk commented 3 years ago

I have not used the Java client nor I know if the code using it has done any additional configuration so I cannot comment on this. If the JS client is running in the browser it would have a bunch of headers filled out by default (including the Origin header). Looking at this:

               services.AddCors(options => options.AddPolicy("Cors", builder =>
                {
                    builder
                        .WithOrigins(
                                    "http://localhost:4200",
                                    "https://stage.blabla.com",
                                    "http://stage.blabla.com")
                        .AllowAnyMethod()
                        .AllowAnyHeader()
                        .AllowCredentials();
                }));

you allow only specific origins. The client is not setting any headers on your behalf. Likely, you need to set the Origin header to match any of the ones you allow.

govi2010 commented 3 years ago

@moozzyk you are right . I need to set Origin header.