-
-
Save gromanev/4da63142825798927375a3da342e65b8 to your computer and use it in GitHub Desktop.
IdentityServer4
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class AuthConstants | |
{ | |
public const string ApiSecret = "asdf753asdf"; | |
public const string ApiV1Scope = "api1"; | |
public static string AuthServerLocation | |
{ | |
get | |
{ | |
#if DEBUG | |
return "http://localhost:5000"; | |
#endif | |
#if RELEASE | |
return "http://production.mydomain.com"; | |
#endif | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static class IdentityServerConfiguration | |
{ | |
/// <summary> | |
/// Возвращает список API - ресурсов, доступ к которым предоставляет IdentityServer4. | |
/// </summary> | |
/// <param name="apiSecret">API-Secret.</param> | |
/// <returns></returns> | |
public static IEnumerable<ApiResource> GetApiResources(string apiSecret) | |
{ | |
if (string.IsNullOrEmpty(apiSecret)) | |
throw new ArgumentNullException(nameof(apiSecret)); | |
var result = new[] | |
{ | |
new ApiResource(AuthConstants.ApiV1Scope, "Lynx MRP System API") | |
{ | |
ApiSecrets = {new Secret(apiSecret.Sha256())}, | |
UserClaims = new List<string> | |
{ | |
"role", | |
"email", | |
"name" | |
} | |
} | |
}; | |
return result; | |
} | |
/// <summary> | |
/// Возвращает список API-клиентов, которым предоставляет доступ IdentityServer4. | |
/// </summary> | |
/// <param name="frontEndLocations">Все расположения Front-end для корректной работы CORS.</param> | |
/// <returns></returns> | |
public static IEnumerable<Client> GetClients() | |
{ | |
return new[] | |
{ | |
new Client | |
{ | |
ClientId = "client", | |
ClientSecrets ={new Secret(AuthConstants.ApiSecret.Sha256())}, | |
AllowedScopes = {GetApiResources(AuthConstants.ApiSecret).First().Name}, | |
RequireClientSecret = false, | |
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, | |
AllowOfflineAccess = true, | |
AllowAccessTokensViaBrowser = true, | |
UpdateAccessTokenClaimsOnRefresh = true, | |
//AllowedCorsOrigins = frontEndLocations, | |
RefreshTokenUsage = TokenUsage.OneTimeOnly, | |
AccessTokenType = AccessTokenType.Jwt, | |
AccessTokenLifetime = 10, // время жизни токена в секндах | |
AbsoluteRefreshTokenLifetime = 2592000 | |
} | |
}; | |
} | |
/// <summary> | |
/// Возвращает сертификат для подписи токенов. | |
/// </summary> | |
/// <exception cref="ConfigurationException">Выбрасывается при отсутствии настройки, либо при некорректной настройке.</exception> | |
/// <returns>Всегда возвращает экземпляр <see cref="X509Certificate2"/>.</returns> | |
public static X509Certificate2 GetSigningCertificate() | |
{ | |
var location = "myCert.pfx"; | |
var password = "asdf753asdf123"; | |
//var tmp = env.ContentRootPath; | |
if (!string.IsNullOrEmpty(location) && !string.IsNullOrEmpty(password)) | |
{ | |
var certFileInfo = new FileInfo(location); | |
if (certFileInfo.Exists) | |
{ | |
var cert = new X509Certificate2( | |
certFileInfo.FullName, | |
password, | |
X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet); | |
return cert; | |
} | |
} | |
throw new Exception("MISSING OR WRONG SIGNING CERTIFICATE CONFIGURATION!!!"); | |
} | |
public static IEnumerable<IdentityResource> GetIdentityResources() | |
{ | |
return new List<IdentityResource> | |
{ | |
new IdentityResources.OpenId(), | |
new IdentityResources.Profile(), | |
}; | |
} | |
public static List<TestUser> GetUsers() | |
{ | |
return new List<TestUser> | |
{ | |
new TestUser | |
{ | |
SubjectId = "1", | |
Username = "alice", | |
Password = "password" | |
}, | |
new TestUser | |
{ | |
SubjectId = "2", | |
Username = "bob", | |
Password = "password" | |
} | |
}; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class LynxProfileService: IProfileService | |
{ | |
private readonly IUserSevice _userService; | |
public LynxProfileService(IUserSevice userService) | |
{ | |
if (userService == null) | |
throw new ArgumentNullException(nameof(userService)); | |
_userService = userService; | |
} | |
public async Task GetProfileDataAsync(ProfileDataRequestContext context) | |
{ | |
var id = int.Parse(context.Subject?.GetSubjectId() ?? "0"); | |
if (id > 0) | |
{ | |
var user = await _userService.GetUserByIdAsync(id); | |
if (user != null) | |
{ | |
context.AddFilteredClaims(UserToClaims(user)); | |
} | |
} | |
else | |
{ | |
throw new Exception("USER NOT FOUND!!!"); | |
} | |
} | |
public async Task IsActiveAsync(IsActiveContext context) | |
{ | |
var id = int.Parse(context.Subject?.GetSubjectId() ?? "0"); | |
Users user = null; | |
if (id > 0) | |
user = await _userService.GetUserByIdAsync(id); | |
context.IsActive = user != null; | |
} | |
/// <summary> | |
/// Преобразует свойства пользователя в массив <see cref="Claim"/>. | |
/// </summary> | |
/// <returns></returns> | |
private Claim[] UserToClaims(Users user) | |
{ | |
var result = new List<Claim> | |
{ | |
new Claim( | |
"name", | |
$"{user.FirstName} {user.SecondName} {user.Patronymic}", | |
"string"), | |
new Claim( | |
"email", | |
user.Email, | |
"string"), | |
new Claim( | |
"role", | |
user.Role?.RoleName.ToLower() ?? "user", | |
"string") | |
}; | |
return result.ToArray(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class LynxResourceOwnerPasswordValidator: IResourceOwnerPasswordValidator | |
{ | |
private readonly IUserSevice UserService; | |
public LynxResourceOwnerPasswordValidator(IUserSevice userService) | |
{ | |
if (userService == null) | |
throw new ArgumentNullException(nameof(userService)); | |
UserService = userService; | |
} | |
public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context) | |
{ | |
var userId = await UserService.FindUserIdAsync(context.UserName, context.Password); | |
if (userId > 0) | |
{ | |
context.Result = new GrantValidationResult( | |
userId.ToString(), | |
OidcConstants.AuthenticationMethods.Password); | |
} | |
else | |
{ | |
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Startup | |
{ | |
... | |
public void ConfigureServices(IServiceCollection services) | |
{ | |
services.AddIdentityServer() | |
.AddProfileService<LynxProfileService>() // вытаскивание профиля пользователя | |
.AddResourceOwnerValidator<LynxResourceOwnerPasswordValidator>() // управление проверкой пароля | |
.AddSigningCredential(IdentityServerConfiguration.GetSigningCertificate()) // регистрация сертификата | |
.AddInMemoryPersistedGrants() // ? | |
.AddInMemoryApiResources(IdentityServerConfiguration.GetApiResources(AuthConstants.ApiSecret)) // ресурсы-апи | |
.AddInMemoryClients(IdentityServerConfiguration.GetClients()); // контроль клиентов | |
} | |
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IServiceProvider serviceProvider) | |
{ | |
... | |
app.UseIdentityServer(); | |
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions | |
{ | |
Authority = AuthConstants.AuthServerLocation, | |
AllowedScopes = { AuthConstants.ApiV1Scope }, | |
RequireHttpsMetadata = false, | |
AutomaticAuthenticate = true, | |
AutomaticChallenge = true, | |
NameClaimType = "email", | |
RoleClaimType = "role" | |
}); | |
... | |
} | |
... | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment