Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
527 views
in Technique[技术] by (71.8m points)

c# - Connect to Google api via asp .net core causes endless authorization loop

I am currently trying to set up an Asp .net core 3 web project to connect to Google calendar and request user data after the user has logged in.

The user logs in and the application requests permission to access their data. The problems start after that.

start up

   // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlite(
                Configuration.GetConnectionString("DefaultConnection")));
        services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
            .AddEntityFrameworkStores<ApplicationDbContext>();
        services.AddControllersWithViews();
        services.AddRazorPages();

        services
            .AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
            .AddCookie()
            .AddGoogleOpenIdConnect(options =>
            {
                IConfigurationSection googleAuthSection = Configuration.GetSection("Authentication:Google");
                options.ClientId = googleAuthSection["ClientId"];
                options.ClientSecret = googleAuthSection["ClientSecret"];
                options.Scope.Add(Google.Apis.Calendar.v3.CalendarService.Scope.Calendar);
            });
    }

controller

public class CalController : Controller
{
    private readonly ILogger<CalController> _logger;

    public CalController(ILogger<CalController> logger)
    {
        _logger = logger;
    }

    [Authorize]
    public async Task<IActionResult> Index([FromServices] IGoogleAuthProvider auth)
    {
        var cred = await auth.GetCredentialAsync();
        var service = new CalendarService(new BaseClientService.Initializer
        {
            HttpClientInitializer = cred
        });
        var calendar = await service.Calendars.Get("primary").ExecuteAsync();

        return View();
    }
   
}

Issue

Currently the system is looping on me. When i navigate to the calendar controller It comes with the following error.

enter image description here

So i created an account controller with the following action.

 public IActionResult Logins([FromQuery] string returnURL)
    {
        return RedirectToAction("Index", "Cal");           
        
    }

Which now just causes the whole thing to loop. Why isn't the authorize attribute detecting that it is logged in and authenticated?

strange thing

If I remove the authorize attribute. Login the user and go directly to the cal controller i have access to their data and it all works.

But as soon as i add the authorize attributed it cant detect that it is authenticated.

Google .net client library.

I originally posted this over on the Google .net client libary 1584 unfortunately the team was not able to assist in getting this to work with asp .net core even though it is supposed to work.

I suspect there is something wrong with my setup but i am at a lost to figure out what the issue could be.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I finally got this working.

startup.cs

public class Client
    {
        public class Web
        {
            public string client_id { get; set; }
            public string client_secret { get; set; }
        }

        public Web web { get; set; }
    }



    public class ClientInfo
    {
        public Client Client { get; set;  }

        private readonly IConfiguration _configuration;

        public ClientInfo(IConfiguration configuration)
        {
            _configuration = configuration;
            Client = Load();
        }

        private Client Load()
        {
            var filePath = _configuration["TEST_WEB_CLIENT_SECRET_FILENAME"];
            if (string.IsNullOrEmpty(filePath))
            {
                throw new InvalidOperationException(
                    $"Please set the TEST_WEB_CLIENT_SECRET_FILENAME environment variable before running tests.");
            }

            if (!File.Exists(filePath))
            {
                throw new InvalidOperationException(
                    $"Please set the TEST_WEB_CLIENT_SECRET_FILENAME environment variable before running tests.");
            }

            var x = File.ReadAllText(filePath);

            return JsonConvert.DeserializeObject<Client>(File.ReadAllText(filePath));
        }
    }

    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.AddSingleton<ClientInfo>();

            services.AddControllers();

            services.AddAuthentication(o =>
                {
                    // This is for challenges to go directly to the Google OpenID Handler, so there's no
                    // need to add an AccountController that emits challenges for Login.
                    o.DefaultChallengeScheme = GoogleOpenIdConnectDefaults.AuthenticationScheme;
                    // This is for forbids to go directly to the Google OpenID Handler, which checks if
                    // extra scopes are required and does automatic incremental auth.
                    o.DefaultForbidScheme = GoogleOpenIdConnectDefaults.AuthenticationScheme;
                    o.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                })
                .AddCookie()
                .AddGoogleOpenIdConnect(options =>
                {
                    var clientInfo = new ClientInfo(Configuration);
                    options.ClientId = clientInfo.Client.web.client_id;
                    options.ClientSecret = clientInfo.Client.web.client_secret;
                });
            services.AddMvc();
        }


        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();

            app.UseRouting();
            
            app.UseAuthentication();
            app.UseAuthorization();

            app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
        }
    }

Controller with Auth

[ApiController]
[Route("[controller]")]
public class GAAnalyticsController : ControllerBase
{
    
    private readonly ILogger<WeatherForecastController> _logger;

    public GAAnalyticsController(ILogger<WeatherForecastController> logger)
    {
        _logger = logger;
    }

    // Test showing use of incremental auth.
    // This attribute states that the listed scope(s) must be authorized in the handler.
    [GoogleScopedAuthorize(AnalyticsReportingService.ScopeConstants.AnalyticsReadonly)]
    public async Task<GetReportsResponse> Get([FromServices] IGoogleAuthProvider auth, [FromServices] ClientInfo clientInfo)
    {
        var GoogleAnalyticsViewId = "78110423";

        var cred = await auth.GetCredentialAsync();
        var service = new  AnalyticsReportingService(new BaseClientService.Initializer
        {
            HttpClientInitializer = cred
        });
        
        var dateRange = new DateRange
        {
            StartDate = "2015-06-15",
            EndDate = "2015-06-30"
        };

        // Create the Metrics object.
        var sessions = new Metric
        {
            Expression = "ga:sessions",
            Alias = "Sessions"
        };

        //Create the Dimensions object.
        var browser = new Dimension
        {
            Name = "ga:browser"
        };

        // Create the ReportRequest object.
        var reportRequest = new ReportRequest
        {
            ViewId = GoogleAnalyticsViewId,
            DateRanges = new List<DateRange> {dateRange},
            Dimensions = new List<Dimension> {browser},
            Metrics = new List<Metric> {sessions}
        };

        var requests = new List<ReportRequest> {reportRequest};

        // Create the GetReportsRequest object.
        var getReport = new GetReportsRequest {ReportRequests = requests};

        // Make the request.
        var response = service.Reports.BatchGet(getReport).Execute();
        return response;
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...