I am trying to implement Oauth in my .net API with EntityFramework. I already completed the custom local authentication with JWT tokens and refresh tokens. I got a little bit confused as to how I would approach it.
Here is my implementation for the Google Oauth system (I also use Facebook Oauth but it's kinda the same thing):
[HttpGet("google-login")][AllowAnonymous]public IActionResult GoogleLogin([FromQuery] string returnUrl){ var redirectUrl = Url.Action(nameof(GoogleResponse), "Auth", new { returnUrl }, Request.Scheme); var properties = new AuthenticationProperties { RedirectUri = redirectUrl }; return Challenge(properties, GoogleDefaults.AuthenticationScheme);}[HttpGet("signin-google")][AllowAnonymous]public async Task<IActionResult> GoogleResponse([FromQuery] string returnUrl){ var authenticateResult = await HttpContext.AuthenticateAsync(GoogleDefaults.AuthenticationScheme); if (!authenticateResult.Succeeded) return BadRequest("Google authentication failed."); var claims = authenticateResult.Principal.Identities.FirstOrDefault()?.Claims; var email = claims?.FirstOrDefault(c => c.Type == ClaimTypes.Email)?.Value; var name = claims?.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value; var key = claims?.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value; var ipAddress = HttpContext.Connection.RemoteIpAddress.MapToIPv6().ToString(); if (string.IsNullOrEmpty(email)) return BadRequest("Email not found"); var result = await authService.SignInWithProviderAsync(email, key, ipAddress, "google"); return result.Match<IActionResult, OauthResponse>(success => { var result = success.Data; SetAccessTokenInResponse(result.Jwt); SetRefreshTokenInResponse(result.RefreshToken); var redirectUri = $"{returnUrl}?access_token={result.Jwt}&refresh_token={result.RefreshToken}"; return Redirect(redirectUri); }, BadRequest);}and this is the program.cs setting for oauth:
builder.Services.AddAuthentication(options =>{ options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;}).AddJwtBearer(options =>{ options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = builder.Configuration["JwtConfig:Issuer"], ValidAudience = builder.Configuration["JwtConfig:Audience"], IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["JwtConfig:Key"])) }; options.Events = new JwtBearerEvents { OnMessageReceived = context => { context.Token = context.Request.Cookies["tmy209w1"]; return Task.CompletedTask; } };}).AddGoogle(options =>{ options.ClientId = builder.Configuration["Authentication:Google:ClientId"]; options.ClientSecret = builder.Configuration["Authentication:Google:ClientSecret"]; options.CallbackPath = "/signin-google"; options.SaveTokens = false;}).AddFacebook(options =>{ options.ClientId = builder.Configuration["Authentication:Facebook:AppId"]; options.ClientSecret = builder.Configuration["Authentication:Facebook:AppSecret"]; options.CallbackPath = "/signin-facebook"; options.SaveTokens = false;});I am not sure if this is how it's supposed to go so correct me if anything is wrong with the implementation.
I don't want to use the token / cookie that the Oauth middleware issues because I already have a custom token that I want to issue to the user, but I keep finding this persistent cookie name identity.external and I don't know why it's persisting.
Please help me get this to work properly the way it's meant to work.
- 4\$\begingroup\$If your code isn't working the way you expect, this is more of a Stack Overflow question than a Code Review question.\$\endgroup\$Chris– Chris2025-02-03 23:09:16 +00:00CommentedFeb 3 at 23:09
- 1\$\begingroup\$@Chris thanks for replying , the code is running correctly I am just questioning some aspects , like if I am implementing this the right way and some issues I am facing\$\endgroup\$Lemon juice– Lemon juice2025-02-03 23:15:20 +00:00CommentedFeb 3 at 23:15
- \$\begingroup\$You can use
ConfigureApplicationCookieandConfigureExternalCookieto configure cookies.\$\endgroup\$iSR5– iSR52025-02-04 06:03:01 +00:00CommentedFeb 4 at 6:03
1 Answer1
Route templates
Thegoogle-login andsignin-google seem a bit weird to me:
- login and sign-in are usually considered as synonyms (or least used interchangeably)
- they follow different naming conventions even though they are tightly coupled
Try to use the same naming conventions (like prefix both routes withgoogle-) and use more expressive suffixes.
The same could/should be applied for the method names as well.
Usage ofHttpContext
For better testability rely on theIHttpContextAccessor to access theHttpContext rather than through theControllerBase
Null conditional operator
I personally don't like to use multiple?. in a single line. I would rather suggest an early exit than having null conditional operators everywhere.
Constructing URl
var redirectUri = $"{returnUrl}?access_token={result.Jwt}&refresh_token={result.RefreshToken}";If possible, do not construct URL by hand. It is error-prone, for example if yourreturnUrl already contains a query string then this string concatenation will generate an invalid url.
Instead useUriBuilder andHttpUtility.ParseQueryString
var uriBuilder = new UriBuilder(returnUrl);var query = HttpUtility.ParseQueryString(uriBuilder.Query);query["access_token"] = result.Jwt;query["refresh_token"] = "result.RefreshToken";uriBuilder.Query = query.ToString();return Redirect(uriBuilder.ToString());You mustlog in to answer this question.
Explore related questions
See similar questions with these tags.


