Search code examples
.net-coreoauth-2.0blazorlinkedin-apiblazor-server-side

Blazor with LinkedIn OAuth 2 "The redirect_uri does not match the registered value" or "Err too many redirections"


I'm trying to implement LinkedIn Sign In OAuth 2 with Blazor Server Side.

But I have a problem.

When I declare the authorized redirect URLs for my app as "https://localhost:7167/signin-linkedin" I got an error "The redirect_uri does not match the registered value"

I checked the redirect_uri value in the URI parameter, it's "https://localhost:7167/login/signin-linkedin"

I added this URI as authorized redirect URLs, the login works fine now, but just after the sign-in, there is a cyclic redirect. And the browser throws an error "too many redirections"

program.cs

builder.Services.AddAuthentication(options =>
   {
       options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
   })
   .AddCookie(o =>
   {
       o.LoginPath = new PathString("/login");
       o.LogoutPath = new PathString("/logout");
   })
   .AddOAuth("LinkedIn", options =>
   {
       options.ClientId = "sampletest";
       options.ClientSecret = "sampletest";
       options.ClaimsIssuer = LinkedInAuthenticationDefaults.Issuer;
       options.CallbackPath = new PathString("/signin-linkedin"); //LinkedInAuthenticationDefaults.CallbackPath;
       options.AuthorizationEndpoint = LinkedInAuthenticationDefaults.AuthorizationEndpoint;
       options.TokenEndpoint = LinkedInAuthenticationDefaults.TokenEndpoint;
       options.UserInformationEndpoint = LinkedInAuthenticationDefaults.UserInformationEndpoint;
       options.Scope.Add("r_liteprofile");
       options.Scope.Add("r_emailaddress");
       options.Events = new OAuthEvents
       {
           OnCreatingTicket = async context =>
           {
               var request = new HttpRequestMessage(HttpMethod.Get, context.Options.UserInformationEndpoint);
               request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", context.AccessToken);
               request.Headers.Add("x-li-format", "json");

               var response = await context.Backchannel.SendAsync(request, context.HttpContext.RequestAborted);
               response.EnsureSuccessStatusCode();

               var user = JObject.Parse(await response.Content.ReadAsStringAsync());
               var userId = user.Value<string>("id");

               if (!string.IsNullOrEmpty(userId))
                   context.Identity?.AddClaim(new Claim(ClaimTypes.NameIdentifier, userId, ClaimValueTypes.String,
                       context.Options.ClaimsIssuer));

               var formattedName = user.Value<string>("formattedName");
               if (!string.IsNullOrEmpty(formattedName))
                   context.Identity?.AddClaim(new Claim(ClaimTypes.Name, formattedName, ClaimValueTypes.String,
                       context.Options.ClaimsIssuer));

               var email = user.Value<string>("emailAddress");
               if (!string.IsNullOrEmpty(email))
                   context.Identity?.AddClaim(new Claim(ClaimTypes.Email, email, ClaimValueTypes.String,
                       context.Options.ClaimsIssuer));

               var pictureUrl = user.Value<string>("pictureUrl");
               if (!string.IsNullOrEmpty(email))
                   context.Identity?.AddClaim(new Claim("profile-picture", pictureUrl, ClaimValueTypes.String,
                       context.Options.ClaimsIssuer));
           }
       };
   });


app.Map("/login", applicationBuilder =>
{
   applicationBuilder.Run(async context =>
   {
       await context.ChallengeAsync("LinkedIn",
           properties: new AuthenticationProperties { RedirectUri = "/" });
   });
});

app.Map("/logout", applicationBuilder =>
{
   applicationBuilder.Run(async context =>
   {
       await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
       context.Response.Redirect("/");
   });
});

Solution

  • Ok, there is a weird behavior with Blazor routing. Blazor add the callback path to the current path. There is nothing to do.

    I just add a controller with routing, and it works. The callback path is added to base uri.