Next.JS / Clerk.com 和使用 .net Web API 进行身份验证

问题描述 投票:0回答:1

我是 React/next.js 新手,我正在尝试将我的应用程序设置为使用后端 .NET 8 WEB API。

我通过此链接设置了 WEB API。

这个效果很好。当我在 VS 2022 中启动 WEB API 时,它要求我登录 Clerk.com,然后显示我的 WEB API 调用。

现在我尝试通过我的 next.js 应用程序调用相同的 WEB API。

最后,我希望用户登录我的 next.js 应用程序,然后使用该登录验证传递到我的 .NET 8 WEB API 以登录该应用程序,然后能够与 WEB API 正确对话。

现在,我必须确保我已退出 WEB API,然后通过 Clerk.com 登录 next.js。然后单击调用 WEB API 的按钮,它要求我再次登录 Clerk.com。 完成此操作后,它会收到 302 已找到。似乎没有传递进行身份验证所需的项目?

有什么想法吗?

这是我的 next.js 代码:

"use client" import { useEffect, useState } from 'react'; import { WeatherForecast } from '../interfaces/WeatherForecastModel'; // Adjust the import path as necessary import { enableRipple } from '@syncfusion/ej2-base'; import { ButtonComponent } from '@syncfusion/ej2-react-buttons' import { useAuth, useClerk } from '@clerk/clerk-react' enableRipple(true); const Index = () => { const { getToken } = useAuth() const [token, setToken] = useState(''); // Manage token with useState const [clerkOther, setclerkOther] = useState(''); // Manage token with useState const [data, setData] = useState<WeatherForecast[]>([]); const { user } = useClerk(); const fetchData = async (url: string) => { const token = await getToken(); const clerkOther = user?.publicMetadata.clerk_token as string; setToken(token ?? ''); // Update the token state setclerkOther(clerkOther); // Update the token state const response = await fetch(url, { method: 'GET', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}`, }, mode: 'no-cors', }); if (!response.ok) { throw new Error('Network response was not ok'); } return response.json(); }; const clickEventHandlerWeather = async () => { try { const jsonData = await fetchData('https://localhost:7160/WeatherForecast'); setData(jsonData); } catch (error) { console.error('Failed to fetch weather data:', error); } }; const clickEventHandlerLogin = async () => { try { await fetchData('https://localhost:7160/Login'); // For login, assuming you might not want to set any specific data } catch (error) { console.error('Failed to login:', error); } }; return ( <div className="row"> <div className='col-md-6'> <ButtonComponent cssClass='e-danger' onClick={clickEventHandlerLogin}>Login</ButtonComponent> </div> <br></br><br></br> <div className='col-md-6'> <ButtonComponent cssClass='e-danger' onClick={clickEventHandlerWeather}>Weather</ButtonComponent> </div> {/* Display token. Ensure you handle displaying sensitive information securely */} <div style={{ width: '200px' }}>{token}</div> <br></br> <div style={{ width: '200px' }}>clerkOther: {token}</div> </div> ); }; export default Index;

注意:另一个问题是 CORS。我知道我不需要这样做,但这也导致了其他错误。所以我打算在即将使用 WEB API 时进行研究。

reactjs asp.net-core next.js asp.net-core-webapi .net-8.0
1个回答
0
投票
manual-jwt

Clerk.net Program.cs 的一部分

builder.Services.AddClerkApiClient(config => { config.SecretKey = builder.Configuration["ClerkSecretKey"]; }); builder.Services.AddJwtBearerAuthentication(builder.Configuration, builder.Services.BuildServiceProvider().GetRequiredService<IHttpClientFactory>());

应用 JWT 检查(这可以在某些时候重构)

namespace WebAPINext.Configuration { public static class JwtBearerAuthenticationConfiguration { public static void AddJwtBearerAuthentication( this IServiceCollection services, IConfiguration configuration, IHttpClientFactory httpClientFactory) { services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { // Assume publicKey is your Clerk instance's public RSA key in PEM format var pemFormat = configuration["ClerkPublicKeyPemFormat"]; if (pemFormat == null) { throw new Exception(nameof(pemFormat)); } RSA rsa; try { rsa = RSA.Create(); rsa.ImportFromPem(pemFormat.ToCharArray()); } catch (System.ArgumentException) { throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.BadRequest) { Content = new StringContent("Invalid RSA key format."), ReasonPhrase = "Invalid Request" }); } options.Authority = configuration["ClerkAuthority"]; options.TokenValidationParameters = new TokenValidationParameters { ValidateAudience = false, ValidateIssuerSigningKey = true, ValidateLifetime = true, IssuerSigningKey = new RsaSecurityKey(rsa), ValidateIssuer = false, ClockSkew = TimeSpan.Zero }; options.Events = new JwtBearerEvents { OnTokenValidated = async context => { var azp = context.Principal?.FindFirstValue("azp"); if (string.IsNullOrEmpty(azp) || !azp.Equals(configuration["ClerkAuthorizedParty"])) { context.Fail("AZP Claim is invalid or missing"); return; } await SetClaims(context, configuration, httpClientFactory).ConfigureAwait(false); }, }; }); } // Claims can be saved in JWT via Clerk.com private static async Task SetClaims(TokenValidatedContext context, IConfiguration configuration, IHttpClientFactory httpClientFactory) { try { // Assuming 'configuration' and 'context' are available in the scope var clerkApiKey = configuration["ClerkSecretKey"]; if (string.IsNullOrEmpty(clerkApiKey)) { throw new InvalidOperationException("Clerk API key is not configured properly."); } var userIdClaim = context.Principal.Claims.FirstOrDefault(c => c.Type == "user_id" || c.Type == ClaimTypes.NameIdentifier); if (userIdClaim == null || string.IsNullOrEmpty(userIdClaim.Value)) { throw new InvalidOperationException("User ID claim is not available."); } string userId = userIdClaim.Value; string clerkApiUrl = $"https://api.clerk.com/v1/users/{userId}"; using var client = httpClientFactory.CreateClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", clerkApiKey); var response = await client.GetAsync(clerkApiUrl); if (!response.IsSuccessStatusCode) { Console.WriteLine("Failed to retrieve user details from Clerk."); return; } var responseBody = await response.Content.ReadAsStringAsync(); using var jsonDoc = JsonDocument.Parse(responseBody); var user = jsonDoc.RootElement; var id = user.GetProperty("id").GetString(); var emailAddress = user.GetProperty("email_addresses")[0].GetProperty("email_address").GetString(); var firstName = user.GetProperty("first_name").GetString(); var lastName = user.GetProperty("last_name").GetString(); var userName = user.GetProperty("username").GetString(); var claims = new List<Claim> { new Claim(ClaimTypes.NameIdentifier, id), new Claim(ClaimTypes.Name, userName), new Claim("firstName", firstName), new Claim("lastName", lastName), new Claim(ClaimTypes.Email, emailAddress), }; if (context.Principal.Identity is ClaimsIdentity claimsIdentity) { claimsIdentity.AddClaims(claims); } } catch (Exception ex) { Console.WriteLine($"Error in VerifyTokenWithClerkAsync: {ex.Message}"); } } } }

© www.soinside.com 2019 - 2024. All rights reserved.