Identity Server4 404 при редиректе после connect/authorize
Я пытаюсь сделать успешный коллбек после connect/authorize, но получаю ошибку при редиректе 404 (приложу скрин).
У меня есть мой Web API это на порту 7142 и приложение на Blazor это на порту 5001. IdentityServer на 7142 живет.
Вот как настроен мой клнфиг клиента:
/// <summary>
/// Класс факторки конфигурации IdentityServer.
/// </summary>
public static class IdentityConfigurationFactory
{
/// <summary>
/// Метод инициализирует области API.
/// </summary>
/// <returns>Спиок областей.</returns>
public static IEnumerable<ApiScope> CreateApiScopes()
{
return new List<ApiScope>
{
new ("api", "Supernova.Identity API"),
};
}
/// <summary>
/// Метод инициализирует клиентов.
/// </summary>
/// <returns>Список клиентов.</returns>
public static IEnumerable<Client> CreateClients()
{
return new List<Client>
{
new ()
{
ClientId = "identity_client",
AllowedGrantTypes = GrantTypes.Code,
ClientSecrets =
{
new Secret("identity_secret".Sha256()),
},
AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"api",
},
RedirectUris = { "https://localhost:7142/signin-oidc" },
AccessTokenLifetime = 600, // 10 мин.,
AllowOfflineAccess = true,
Enabled = true,
// RequireConsent = true,
// AllowRememberConsent = false,
// RedirectUris = new List<string> { "https://localhost:7142/signin-oidc" },
PostLogoutRedirectUris = { "https://localhost:7142/signout-callback-oidc" },
AccessTokenType = AccessTokenType.Jwt,
// RequirePkce = true,
},
};
}
/// <summary>
/// Метод инициализирует ресурсы API.
/// </summary>
/// <returns>Список ресурсов API.</returns>
public static IEnumerable<IdentityResource> CreateGetApiResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
};
}
}
Вот весь мой код авторизации пользователя:
var context = await _interaction.GetAuthorizationContextAsync(authorizeInput.ReturnUrl);
// validate username/password against in-memory store
if (_users.ValidateCredentials(authorizeInput.UserName, authorizeInput.Password))
{
var user = _users.FindByUsername(authorizeInput.UserName);
AuthenticationProperties? props = null;
var isuser = new IdentityServerUser(user.SubjectId)
{
DisplayName = user.Username,
};
await HttpContext.SignInAsync(isuser, props);
}
return Redirect(authorizeInput.ReturnUrl);
Вот мой код приложения Blazor:
private async Task AuthorizeAsync()
{
var currentUrl = NavManager.Uri;
const string clientId = "identity_client";
const string responseType = "code";
const string redirectUri = "https://localhost:7142/signin-oidc";
const string scope = "api";
var state = HttpUtility.ParseQueryString(currentUrl).Get("state");
var codeChallenge = HttpUtility.ParseQueryString(currentUrl).Get("code_challenge");
var codeChallengeMethod = HttpUtility.ParseQueryString(currentUrl).Get("code_challenge_method");
var returnUrlBuilder = new StringBuilder();
returnUrlBuilder.Append("/connect/authorize/callback?");
returnUrlBuilder.Append($"client_id={clientId}&");
returnUrlBuilder.Append($"response_type={responseType}&");
returnUrlBuilder.Append($"redirect_uri={redirectUri}&");
returnUrlBuilder.Append($"state={state}&");
returnUrlBuilder.Append($"code_challenge={codeChallenge}&");
returnUrlBuilder.Append($"code_challenge_method={codeChallengeMethod}&");
returnUrlBuilder.Append($"scope={scope}");
var authorizeInput = new AuthorizeInput
{
UserName = userName,
Password = password,
ReturnUrl = returnUrlBuilder.ToString()
};
await HttpClient.PostAsJsonAsync("https://localhost:7142/api/Identity/Authorize", authorizeInput);
}
Вот как настроен файл Program. У меня .NET 6.
// Добавляем IdentityServer.
builder.Services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryIdentityResources(IdentityConfigurationFactory.CreateGetApiResources())
.AddInMemoryApiScopes(IdentityConfigurationFactory.CreateApiScopes())
.AddInMemoryClients(IdentityConfigurationFactory.CreateClients())
.AddTestUsers(TestUsers.Users);
builder.Services.AddSwaggerGen(opt =>
{
opt.SwaggerDoc("v1", new OpenApiInfo
{
Title = "Supernova.Identity.API",
Version = "v1",
});
opt.AddSecurityDefinition(JwtBearerDefaults.AuthenticationScheme, new OpenApiSecurityScheme
{
In = ParameterLocation.Header,
Name = "Authorization",
Type = SecuritySchemeType.OAuth2,
Flows = new OpenApiOAuthFlows
{
AuthorizationCode = new OpenApiOAuthFlow
{
AuthorizationUrl = new Uri(Environment.GetEnvironmentVariable("Authority") !),
TokenUrl = new Uri(Environment.GetEnvironmentVariable("TokenUrl") !),
},
},
});
opt.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer",
},
},
Array.Empty<string>()
},
});
});
// Добавляем и настраиваем Bearer.
builder.Services.AddAuthentication()
.AddJwtBearer(options =>
{
options.Authority = Environment.GetEnvironmentVariable("Authority");
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false,
ValidateLifetime = true,
};
options.RequireHttpsMetadata = false;
})
.AddOpenIdConnect("oidc", "Supernova.Identity IdentityServer", options =>
{
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
options.SignOutScheme = IdentityServerConstants.SignoutScheme;
options.Authority = Environment.GetEnvironmentVariable("Authority");
// options.RequireHttpsMetadata = false; //false for development only
// options.ClientId = "identity_client";
options.ClientId = "interactive.confidential";
options.ClientSecret = "secret";
options.ResponseType = "code";
// options.UsePkce = true;
// options.Scope.Add("profile");
// options.Scope.Add("offline_access");
options.SaveTokens = true;
// options.ResponseMode = "form_post";
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "role",
};
});
builder.Services.AddAutoMapper(typeof(MappingProfile).Assembly);
builder.Services.Register();
builder.Services.AddHttpContextAccessor();
var app = builder.Build();
app.UseRouting();
// app.UseAuthentication();
app.UseIdentityServer();
app.UseAuthorization();
app.UseCors("ApiCorsPolicy");
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
app.UseStaticFiles();
if (builder.Environment.IsDevelopment() || builder.Environment.IsStaging())
{
app.UseSwagger();
app.UseSwaggerUI(opt =>
{
opt.InjectStylesheet("/swagger-custom-ui/custom.css");
var client = IdentityConfigurationFactory.CreateClients().First();
opt.OAuthClientId(client.ClientId);
opt.OAuthClientSecret(client.ClientSecrets.First().Value);
opt.OAuthUsePkce();
});
}
Пожалуйста, прошу помочь разобраться, я в тупике! Любая помощь будет оценена! Спасибо!
Ответы (1 шт):
Если кому то это еще поможет, то эта проблема была из-за того, что token/authorize ожидает, что у вас на веб-странице есть форма (html-тег form), в которую он вернет код. То есть он работает через FormData и ему нужна любая форма, например скрытая просто. Без нее у вас будет эта ошибка.
Вот этой штуки не хватало в моей ситуации.
