mirror of
https://github.com/meysamhadeli/booking-microservices.git
synced 2026-04-10 17:59:38 +08:00
Merge pull request #346 from meysamhadeli/feat/add-support-role-base-policy
feat: add support role base authorization policy
This commit is contained in:
commit
dedf6086fc
@ -28,8 +28,7 @@
|
||||
},
|
||||
"Jwt": {
|
||||
"Authority": "https://localhost:4000",
|
||||
"Audience": "booking-monolith",
|
||||
"RequireHttpsMetadata": false
|
||||
"Audience": "booking-monolith"
|
||||
},
|
||||
"HealthOptions": {
|
||||
"Enabled": false
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
using BookingMonolith.Identity.Identities.Constants;
|
||||
using Duende.IdentityServer;
|
||||
using Duende.IdentityServer.Models;
|
||||
using IdentityModel;
|
||||
|
||||
namespace BookingMonolith.Identity.Configurations;
|
||||
|
||||
@ -11,10 +12,7 @@ public static class Config
|
||||
{
|
||||
new IdentityResources.OpenId(),
|
||||
new IdentityResources.Profile(),
|
||||
new IdentityResources.Email(),
|
||||
new IdentityResources.Phone(),
|
||||
new IdentityResources.Address(),
|
||||
new(Constants.StandardScopes.Roles, new List<string> {"role"})
|
||||
new IdentityResources.Email()
|
||||
};
|
||||
|
||||
|
||||
@ -25,20 +23,34 @@ public static class Config
|
||||
new(Constants.StandardScopes.PassengerApi),
|
||||
new(Constants.StandardScopes.BookingApi),
|
||||
new(Constants.StandardScopes.IdentityApi),
|
||||
new(Constants.StandardScopes.BookingModularMonolith),
|
||||
new(Constants.StandardScopes.BookingMonolith),
|
||||
new(JwtClaimTypes.Role, new List<string> {"role"})
|
||||
};
|
||||
|
||||
|
||||
public static IList<ApiResource> ApiResources =>
|
||||
new List<ApiResource>
|
||||
{
|
||||
new(Constants.StandardScopes.FlightApi),
|
||||
new(Constants.StandardScopes.PassengerApi),
|
||||
new(Constants.StandardScopes.BookingApi),
|
||||
new(Constants.StandardScopes.IdentityApi),
|
||||
new(Constants.StandardScopes.BookingModularMonolith),
|
||||
new(Constants.StandardScopes.BookingMonolith),
|
||||
new(Constants.StandardScopes.FlightApi)
|
||||
{
|
||||
Scopes = { Constants.StandardScopes.FlightApi }
|
||||
},
|
||||
new(Constants.StandardScopes.PassengerApi)
|
||||
{
|
||||
Scopes = { Constants.StandardScopes.PassengerApi }
|
||||
},
|
||||
new(Constants.StandardScopes.BookingApi)
|
||||
{
|
||||
Scopes = { Constants.StandardScopes.BookingApi }
|
||||
},
|
||||
new(Constants.StandardScopes.IdentityApi)
|
||||
{
|
||||
Scopes = { Constants.StandardScopes.IdentityApi }
|
||||
},
|
||||
new(Constants.StandardScopes.BookingMonolith)
|
||||
{
|
||||
Scopes = { Constants.StandardScopes.BookingMonolith }
|
||||
},
|
||||
};
|
||||
|
||||
public static IEnumerable<Client> Clients =>
|
||||
@ -56,15 +68,16 @@ public static class Config
|
||||
{
|
||||
IdentityServerConstants.StandardScopes.OpenId,
|
||||
IdentityServerConstants.StandardScopes.Profile,
|
||||
JwtClaimTypes.Role, // Include roles scope
|
||||
Constants.StandardScopes.FlightApi,
|
||||
Constants.StandardScopes.PassengerApi,
|
||||
Constants.StandardScopes.BookingApi,
|
||||
Constants.StandardScopes.IdentityApi,
|
||||
Constants.StandardScopes.BookingModularMonolith,
|
||||
Constants.StandardScopes.BookingMonolith,
|
||||
},
|
||||
AccessTokenLifetime = 3600, // authorize the client to access protected resources
|
||||
IdentityTokenLifetime = 3600 // authenticate the user
|
||||
IdentityTokenLifetime = 3600, // authenticate the user,
|
||||
AlwaysIncludeUserClaimsInIdToken = true // Include claims in ID token
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
using BookingMonolith.Identity.Identities.Constants;
|
||||
using BookingMonolith.Identity.Identities.Models;
|
||||
using BuildingBlocks.Constants;
|
||||
using BuildingBlocks.Contracts.EventBus.Messages;
|
||||
using BuildingBlocks.Core;
|
||||
using BuildingBlocks.EFCore;
|
||||
@ -43,14 +44,14 @@ public class IdentityDataSeeder : IDataSeeder
|
||||
{
|
||||
if (!await _identityContext.Roles.AnyAsync())
|
||||
{
|
||||
if (await _roleManager.RoleExistsAsync(Constants.Role.Admin) == false)
|
||||
if (await _roleManager.RoleExistsAsync(IdentityConstant.Role.Admin) == false)
|
||||
{
|
||||
await _roleManager.CreateAsync(new Role { Name = Constants.Role.Admin });
|
||||
await _roleManager.CreateAsync(new Role { Name = IdentityConstant.Role.Admin });
|
||||
}
|
||||
|
||||
if (await _roleManager.RoleExistsAsync(Constants.Role.User) == false)
|
||||
if (await _roleManager.RoleExistsAsync(IdentityConstant.Role.User) == false)
|
||||
{
|
||||
await _roleManager.CreateAsync(new Role { Name = Constants.Role.User });
|
||||
await _roleManager.CreateAsync(new Role { Name = IdentityConstant.Role.User });
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -65,7 +66,7 @@ public class IdentityDataSeeder : IDataSeeder
|
||||
|
||||
if (result.Succeeded)
|
||||
{
|
||||
await _userManager.AddToRoleAsync(InitialData.Users.First(), Constants.Role.Admin);
|
||||
await _userManager.AddToRoleAsync(InitialData.Users.First(), IdentityConstant.Role.Admin);
|
||||
|
||||
await _eventDispatcher.SendAsync(
|
||||
new UserCreated(
|
||||
@ -83,7 +84,7 @@ public class IdentityDataSeeder : IDataSeeder
|
||||
|
||||
if (result.Succeeded)
|
||||
{
|
||||
await _userManager.AddToRoleAsync(InitialData.Users.Last(), Constants.Role.User);
|
||||
await _userManager.AddToRoleAsync(InitialData.Users.Last(), IdentityConstant.Role.User);
|
||||
|
||||
await _eventDispatcher.SendAsync(
|
||||
new UserCreated(
|
||||
|
||||
@ -3,6 +3,7 @@ using BookingMonolith.Identity.Data;
|
||||
using BookingMonolith.Identity.Identities.Models;
|
||||
using BuildingBlocks.Web;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
@ -43,6 +44,21 @@ public static class IdentityServerExtensions
|
||||
//ref: https://documentation.openiddict.com/configuration/encryption-and-signing-credentials.html
|
||||
identityServerBuilder.AddDeveloperSigningCredential();
|
||||
|
||||
builder.Services.ConfigureApplicationCookie(options =>
|
||||
{
|
||||
options.Events.OnRedirectToLogin = context =>
|
||||
{
|
||||
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
options.Events.OnRedirectToAccessDenied = context =>
|
||||
{
|
||||
context.Response.StatusCode = StatusCodes.Status403Forbidden;
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
});
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,12 +2,6 @@ namespace BookingMonolith.Identity.Identities.Constants;
|
||||
|
||||
public static class Constants
|
||||
{
|
||||
public static class Role
|
||||
{
|
||||
public const string Admin = "admin";
|
||||
public const string User = "user";
|
||||
}
|
||||
|
||||
public static class StandardScopes
|
||||
{
|
||||
public const string Roles = "roles";
|
||||
@ -15,7 +9,6 @@ public static class Constants
|
||||
public const string PassengerApi = "passenger-api";
|
||||
public const string BookingApi = "booking-api";
|
||||
public const string IdentityApi = "identity-api";
|
||||
public const string BookingModularMonolith = "booking-modular-monolith";
|
||||
public const string BookingMonolith = "booking-monolith";
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
using Ardalis.GuardClauses;
|
||||
using BookingMonolith.Identity.Identities.Exceptions;
|
||||
using BookingMonolith.Identity.Identities.Models;
|
||||
using BuildingBlocks.Constants;
|
||||
using BuildingBlocks.Contracts.EventBus.Messages;
|
||||
using BuildingBlocks.Core;
|
||||
using BuildingBlocks.Core.CQRS;
|
||||
@ -109,7 +110,7 @@ internal class RegisterNewUserHandler : ICommandHandler<RegisterNewUser, Registe
|
||||
};
|
||||
|
||||
var identityResult = await _userManager.CreateAsync(applicationUser, request.Password);
|
||||
var roleResult = await _userManager.AddToRoleAsync(applicationUser, Constants.Constants.Role.User);
|
||||
var roleResult = await _userManager.AddToRoleAsync(applicationUser, IdentityConstant.Role.User);
|
||||
|
||||
if (identityResult.Succeeded == false)
|
||||
{
|
||||
|
||||
@ -17,7 +17,7 @@ grant_type=password
|
||||
&client_secret=secret
|
||||
&username=samh
|
||||
&password=Admin@123456
|
||||
&scope=booking-modular-monolith
|
||||
&scope=booking-modular-monolith role
|
||||
###
|
||||
|
||||
|
||||
|
||||
@ -27,8 +27,7 @@
|
||||
},
|
||||
"Jwt": {
|
||||
"Authority": "https://localhost:3000",
|
||||
"Audience": "booking-modular-monolith",
|
||||
"RequireHttpsMetadata": false
|
||||
"Audience": "booking-modular-monolith"
|
||||
},
|
||||
"PersistMessageOptions": {
|
||||
"Interval": 30,
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
namespace Identity.Configurations;
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Duende.IdentityServer;
|
||||
using Duende.IdentityServer.Models;
|
||||
using Identity.Constants;
|
||||
using Identity.Identity.Constants;
|
||||
using IdentityModel;
|
||||
|
||||
namespace BookingMonolith.Identity.Configurations;
|
||||
|
||||
public static class Config
|
||||
{
|
||||
@ -12,10 +12,7 @@ public static class Config
|
||||
{
|
||||
new IdentityResources.OpenId(),
|
||||
new IdentityResources.Profile(),
|
||||
new IdentityResources.Email(),
|
||||
new IdentityResources.Phone(),
|
||||
new IdentityResources.Address(),
|
||||
new(Constants.StandardScopes.Roles, new List<string> {"role"})
|
||||
new IdentityResources.Email()
|
||||
};
|
||||
|
||||
|
||||
@ -27,17 +24,33 @@ public static class Config
|
||||
new(Constants.StandardScopes.BookingApi),
|
||||
new(Constants.StandardScopes.IdentityApi),
|
||||
new(Constants.StandardScopes.BookingModularMonolith),
|
||||
new(JwtClaimTypes.Role, new List<string> {"role"})
|
||||
};
|
||||
|
||||
|
||||
public static IList<ApiResource> ApiResources =>
|
||||
new List<ApiResource>
|
||||
{
|
||||
new(Constants.StandardScopes.FlightApi),
|
||||
new(Constants.StandardScopes.PassengerApi),
|
||||
new(Constants.StandardScopes.BookingApi),
|
||||
new(Constants.StandardScopes.IdentityApi),
|
||||
new(Constants.StandardScopes.BookingModularMonolith),
|
||||
new(Constants.StandardScopes.FlightApi)
|
||||
{
|
||||
Scopes = { Constants.StandardScopes.FlightApi }
|
||||
},
|
||||
new(Constants.StandardScopes.PassengerApi)
|
||||
{
|
||||
Scopes = { Constants.StandardScopes.PassengerApi }
|
||||
},
|
||||
new(Constants.StandardScopes.BookingApi)
|
||||
{
|
||||
Scopes = { Constants.StandardScopes.BookingApi }
|
||||
},
|
||||
new(Constants.StandardScopes.IdentityApi)
|
||||
{
|
||||
Scopes = { Constants.StandardScopes.IdentityApi }
|
||||
},
|
||||
new(Constants.StandardScopes.BookingModularMonolith)
|
||||
{
|
||||
Scopes = { Constants.StandardScopes.BookingModularMonolith }
|
||||
},
|
||||
};
|
||||
|
||||
public static IEnumerable<Client> Clients =>
|
||||
@ -55,6 +68,7 @@ public static class Config
|
||||
{
|
||||
IdentityServerConstants.StandardScopes.OpenId,
|
||||
IdentityServerConstants.StandardScopes.Profile,
|
||||
JwtClaimTypes.Role, // Include roles scope
|
||||
Constants.StandardScopes.FlightApi,
|
||||
Constants.StandardScopes.PassengerApi,
|
||||
Constants.StandardScopes.BookingApi,
|
||||
@ -62,7 +76,8 @@ public static class Config
|
||||
Constants.StandardScopes.BookingModularMonolith,
|
||||
},
|
||||
AccessTokenLifetime = 3600, // authorize the client to access protected resources
|
||||
IdentityTokenLifetime = 3600 // authenticate the user
|
||||
IdentityTokenLifetime = 3600, // authenticate the user,
|
||||
AlwaysIncludeUserClaimsInIdToken = true // Include claims in ID token
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using BuildingBlocks.Constants;
|
||||
using BuildingBlocks.Contracts.EventBus.Messages;
|
||||
using BuildingBlocks.Core;
|
||||
using BuildingBlocks.EFCore;
|
||||
@ -47,14 +48,14 @@ public class IdentityDataSeeder : IDataSeeder
|
||||
{
|
||||
if (!await _identityContext.Roles.AnyAsync())
|
||||
{
|
||||
if (await _roleManager.RoleExistsAsync(Constants.Role.Admin) == false)
|
||||
if (await _roleManager.RoleExistsAsync(IdentityConstant.Role.Admin) == false)
|
||||
{
|
||||
await _roleManager.CreateAsync(new Role { Name = Constants.Role.Admin });
|
||||
await _roleManager.CreateAsync(new Role { Name = IdentityConstant.Role.Admin });
|
||||
}
|
||||
|
||||
if (await _roleManager.RoleExistsAsync(Constants.Role.User) == false)
|
||||
if (await _roleManager.RoleExistsAsync(IdentityConstant.Role.User) == false)
|
||||
{
|
||||
await _roleManager.CreateAsync(new Role { Name = Constants.Role.User });
|
||||
await _roleManager.CreateAsync(new Role { Name = IdentityConstant.Role.User });
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -69,7 +70,7 @@ public class IdentityDataSeeder : IDataSeeder
|
||||
|
||||
if (result.Succeeded)
|
||||
{
|
||||
await _userManager.AddToRoleAsync(InitialData.Users.First(), Constants.Role.Admin);
|
||||
await _userManager.AddToRoleAsync(InitialData.Users.First(), IdentityConstant.Role.Admin);
|
||||
|
||||
await _eventDispatcher.SendAsync(
|
||||
new UserCreated(
|
||||
@ -87,7 +88,7 @@ public class IdentityDataSeeder : IDataSeeder
|
||||
|
||||
if (result.Succeeded)
|
||||
{
|
||||
await _userManager.AddToRoleAsync(InitialData.Users.Last(), Constants.Role.User);
|
||||
await _userManager.AddToRoleAsync(InitialData.Users.Last(), IdentityConstant.Role.User);
|
||||
|
||||
await _eventDispatcher.SendAsync(
|
||||
new UserCreated(
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
using BookingMonolith.Identity.Configurations;
|
||||
using BuildingBlocks.Web;
|
||||
using Identity.Data;
|
||||
using Identity.Identity.Models;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
@ -44,6 +46,21 @@ public static class IdentityServerExtensions
|
||||
//ref: https://documentation.openiddict.com/configuration/encryption-and-signing-credentials.html
|
||||
identityServerBuilder.AddDeveloperSigningCredential();
|
||||
|
||||
builder.Services.ConfigureApplicationCookie(options =>
|
||||
{
|
||||
options.Events.OnRedirectToLogin = context =>
|
||||
{
|
||||
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
options.Events.OnRedirectToAccessDenied = context =>
|
||||
{
|
||||
context.Response.StatusCode = StatusCodes.Status403Forbidden;
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
});
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,12 +2,6 @@ namespace Identity.Identity.Constants;
|
||||
|
||||
public static class Constants
|
||||
{
|
||||
public static class Role
|
||||
{
|
||||
public const string Admin = "admin";
|
||||
public const string User = "user";
|
||||
}
|
||||
|
||||
public static class StandardScopes
|
||||
{
|
||||
public const string Roles = "roles";
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
using BuildingBlocks.Constants;
|
||||
using Duende.IdentityServer.EntityFramework.Entities;
|
||||
|
||||
namespace Identity.Identity.Features.RegisteringNewUser.V1;
|
||||
@ -114,7 +115,7 @@ internal class RegisterNewUserHandler : ICommandHandler<RegisterNewUser, Registe
|
||||
};
|
||||
|
||||
var identityResult = await _userManager.CreateAsync(applicationUser, request.Password);
|
||||
var roleResult = await _userManager.AddToRoleAsync(applicationUser, Constants.Constants.Role.User);
|
||||
var roleResult = await _userManager.AddToRoleAsync(applicationUser, IdentityConstant.Role.User);
|
||||
|
||||
if (identityResult.Succeeded == false)
|
||||
{
|
||||
|
||||
@ -29,7 +29,7 @@ grant_type=password
|
||||
&client_secret=secret
|
||||
&username=samh
|
||||
&password=Admin@123456
|
||||
&scope=flight-api
|
||||
&scope=flight-api role
|
||||
|
||||
### change scope base on microservices scope (eg. passenger-api, ...)
|
||||
###
|
||||
|
||||
@ -27,9 +27,7 @@
|
||||
},
|
||||
"Jwt": {
|
||||
"Authority": "http://identity:80",
|
||||
"Audience": "booking-api",
|
||||
"RequireHttpsMetadata": false,
|
||||
"MetadataAddress": "http://identity:80/.well-known/openid-configuration"
|
||||
"Audience": "booking-api"
|
||||
},
|
||||
"Grpc": {
|
||||
"FlightAddress": "flight:5003",
|
||||
|
||||
@ -13,9 +13,7 @@
|
||||
},
|
||||
"Jwt": {
|
||||
"Authority": "http://localhost:6005",
|
||||
"Audience": "booking-api",
|
||||
"RequireHttpsMetadata": false,
|
||||
"MetadataAddress": "http://localhost:6005/.well-known/openid-configuration"
|
||||
"Audience": "booking-api"
|
||||
},
|
||||
"RabbitMqOptions": {
|
||||
"HostName": "localhost",
|
||||
|
||||
@ -14,9 +14,7 @@
|
||||
},
|
||||
"Jwt": {
|
||||
"Authority": "http://identity:80",
|
||||
"Audience": "flight-api",
|
||||
"RequireHttpsMetadata": false,
|
||||
"MetadataAddress": "http://identity:80/.well-known/openid-configuration"
|
||||
"Audience": "flight-api"
|
||||
},
|
||||
"RabbitMqOptions": {
|
||||
"HostName": "rabbitmq",
|
||||
|
||||
@ -20,9 +20,7 @@
|
||||
},
|
||||
"Jwt": {
|
||||
"Authority": "http://localhost:6005",
|
||||
"Audience": "flight-api",
|
||||
"RequireHttpsMetadata": false,
|
||||
"MetadataAddress": "http://localhost:6005/.well-known/openid-configuration"
|
||||
"Audience": "flight-api"
|
||||
},
|
||||
"RabbitMqOptions": {
|
||||
"HostName": "localhost",
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
namespace Identity.Configurations;
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Duende.IdentityServer;
|
||||
using Duende.IdentityServer.Models;
|
||||
using Identity.Constants;
|
||||
using Identity.Identity.Constants;
|
||||
using IdentityModel;
|
||||
|
||||
namespace BookingMonolith.Identity.Configurations;
|
||||
|
||||
public static class Config
|
||||
{
|
||||
@ -12,10 +12,7 @@ public static class Config
|
||||
{
|
||||
new IdentityResources.OpenId(),
|
||||
new IdentityResources.Profile(),
|
||||
new IdentityResources.Email(),
|
||||
new IdentityResources.Phone(),
|
||||
new IdentityResources.Address(),
|
||||
new(Constants.StandardScopes.Roles, new List<string> {"role"})
|
||||
new IdentityResources.Email()
|
||||
};
|
||||
|
||||
|
||||
@ -25,17 +22,30 @@ public static class Config
|
||||
new(Constants.StandardScopes.FlightApi),
|
||||
new(Constants.StandardScopes.PassengerApi),
|
||||
new(Constants.StandardScopes.BookingApi),
|
||||
new(Constants.StandardScopes.IdentityApi)
|
||||
new(Constants.StandardScopes.IdentityApi),
|
||||
new(JwtClaimTypes.Role, new List<string> {"role"})
|
||||
};
|
||||
|
||||
|
||||
public static IList<ApiResource> ApiResources =>
|
||||
new List<ApiResource>
|
||||
{
|
||||
new(Constants.StandardScopes.FlightApi),
|
||||
new(Constants.StandardScopes.PassengerApi),
|
||||
new(Constants.StandardScopes.BookingApi),
|
||||
new(Constants.StandardScopes.FlightApi)
|
||||
{
|
||||
Scopes = { Constants.StandardScopes.FlightApi }
|
||||
},
|
||||
new(Constants.StandardScopes.PassengerApi)
|
||||
{
|
||||
Scopes = { Constants.StandardScopes.PassengerApi }
|
||||
},
|
||||
new(Constants.StandardScopes.BookingApi)
|
||||
{
|
||||
Scopes = { Constants.StandardScopes.BookingApi }
|
||||
},
|
||||
new(Constants.StandardScopes.IdentityApi)
|
||||
{
|
||||
Scopes = { Constants.StandardScopes.IdentityApi }
|
||||
},
|
||||
};
|
||||
|
||||
public static IEnumerable<Client> Clients =>
|
||||
@ -53,13 +63,15 @@ public static class Config
|
||||
{
|
||||
IdentityServerConstants.StandardScopes.OpenId,
|
||||
IdentityServerConstants.StandardScopes.Profile,
|
||||
JwtClaimTypes.Role, // Include roles scope
|
||||
Constants.StandardScopes.FlightApi,
|
||||
Constants.StandardScopes.PassengerApi,
|
||||
Constants.StandardScopes.BookingApi,
|
||||
Constants.StandardScopes.IdentityApi
|
||||
Constants.StandardScopes.IdentityApi,
|
||||
},
|
||||
AccessTokenLifetime = 3600, // authorize the client to access protected resources
|
||||
IdentityTokenLifetime = 3600 // authenticate the user
|
||||
IdentityTokenLifetime = 3600, // authenticate the user,
|
||||
AlwaysIncludeUserClaimsInIdToken = true // Include claims in ID token
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using BuildingBlocks.Constants;
|
||||
using BuildingBlocks.Contracts.EventBus.Messages;
|
||||
using BuildingBlocks.Core;
|
||||
using BuildingBlocks.EFCore;
|
||||
@ -43,14 +44,14 @@ public class IdentityDataSeeder : IDataSeeder
|
||||
|
||||
private async Task SeedRoles()
|
||||
{
|
||||
if (await _roleManager.RoleExistsAsync(Constants.Role.Admin) == false)
|
||||
if (await _roleManager.RoleExistsAsync(IdentityConstant.Role.Admin) == false)
|
||||
{
|
||||
await _roleManager.CreateAsync(new Role { Name = Constants.Role.Admin });
|
||||
await _roleManager.CreateAsync(new Role { Name = IdentityConstant.Role.Admin });
|
||||
}
|
||||
|
||||
if (await _roleManager.RoleExistsAsync(Constants.Role.User) == false)
|
||||
if (await _roleManager.RoleExistsAsync(IdentityConstant.Role.User) == false)
|
||||
{
|
||||
await _roleManager.CreateAsync(new Role { Name = Constants.Role.User });
|
||||
await _roleManager.CreateAsync(new Role { Name = IdentityConstant.Role.User });
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,7 +63,7 @@ public class IdentityDataSeeder : IDataSeeder
|
||||
|
||||
if (result.Succeeded)
|
||||
{
|
||||
await _userManager.AddToRoleAsync(InitialData.Users.First(), Constants.Role.Admin);
|
||||
await _userManager.AddToRoleAsync(InitialData.Users.First(), IdentityConstant.Role.Admin);
|
||||
|
||||
await _eventDispatcher.SendAsync(new UserCreated(InitialData.Users.First().Id, InitialData.Users.First().FirstName + " " + InitialData.Users.First().LastName, InitialData.Users.First().PassPortNumber));
|
||||
}
|
||||
@ -74,7 +75,7 @@ public class IdentityDataSeeder : IDataSeeder
|
||||
|
||||
if (result.Succeeded)
|
||||
{
|
||||
await _userManager.AddToRoleAsync(InitialData.Users.Last(), Constants.Role.User);
|
||||
await _userManager.AddToRoleAsync(InitialData.Users.Last(), IdentityConstant.Role.User);
|
||||
|
||||
await _eventDispatcher.SendAsync(new UserCreated(InitialData.Users.Last().Id, InitialData.Users.Last().FirstName + " " + InitialData.Users.Last().LastName, InitialData.Users.Last().PassPortNumber));
|
||||
}
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
using BookingMonolith.Identity.Configurations;
|
||||
using BuildingBlocks.Web;
|
||||
using Identity.Data;
|
||||
using Identity.Identity.Models;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
@ -44,6 +46,21 @@ public static class IdentityServerExtensions
|
||||
//ref: https://documentation.openiddict.com/configuration/encryption-and-signing-credentials.html
|
||||
identityServerBuilder.AddDeveloperSigningCredential();
|
||||
|
||||
builder.Services.ConfigureApplicationCookie(options =>
|
||||
{
|
||||
options.Events.OnRedirectToLogin = context =>
|
||||
{
|
||||
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
options.Events.OnRedirectToAccessDenied = context =>
|
||||
{
|
||||
context.Response.StatusCode = StatusCodes.Status403Forbidden;
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
});
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,12 +2,6 @@ namespace Identity.Identity.Constants;
|
||||
|
||||
public static class Constants
|
||||
{
|
||||
public static class Role
|
||||
{
|
||||
public const string Admin = "admin";
|
||||
public const string User = "user";
|
||||
}
|
||||
|
||||
public static class StandardScopes
|
||||
{
|
||||
public const string Roles = "roles";
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
using BuildingBlocks.Constants;
|
||||
|
||||
namespace Identity.Identity.Features.RegisteringNewUser.V1;
|
||||
|
||||
using System;
|
||||
@ -113,7 +115,7 @@ internal class RegisterNewUserHandler : ICommandHandler<RegisterNewUser, Registe
|
||||
};
|
||||
|
||||
var identityResult = await _userManager.CreateAsync(applicationUser, request.Password);
|
||||
var roleResult = await _userManager.AddToRoleAsync(applicationUser, Constants.Constants.Role.User);
|
||||
var roleResult = await _userManager.AddToRoleAsync(applicationUser, IdentityConstant.Role.User);
|
||||
|
||||
if (identityResult.Succeeded == false)
|
||||
{
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
using BuildingBlocks.Constants;
|
||||
using BuildingBlocks.Contracts.EventBus.Messages;
|
||||
using BuildingBlocks.Core;
|
||||
using BuildingBlocks.EFCore;
|
||||
@ -23,14 +24,14 @@ public class IdentityTestDataSeeder(
|
||||
|
||||
private async Task SeedRoles()
|
||||
{
|
||||
if (await roleManager.RoleExistsAsync(Constants.Role.Admin) == false)
|
||||
if (await roleManager.RoleExistsAsync(IdentityConstant.Role.Admin) == false)
|
||||
{
|
||||
await roleManager.CreateAsync(new Role { Name = Constants.Role.Admin });
|
||||
await roleManager.CreateAsync(new Role { Name = IdentityConstant.Role.Admin });
|
||||
}
|
||||
|
||||
if (await roleManager.RoleExistsAsync(Constants.Role.User) == false)
|
||||
if (await roleManager.RoleExistsAsync(IdentityConstant.Role.User) == false)
|
||||
{
|
||||
await roleManager.CreateAsync(new Role { Name = Constants.Role.User });
|
||||
await roleManager.CreateAsync(new Role { Name = IdentityConstant.Role.User });
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,7 +43,7 @@ public class IdentityTestDataSeeder(
|
||||
|
||||
if (result.Succeeded)
|
||||
{
|
||||
await userManager.AddToRoleAsync(InitialData.Users.First(), Constants.Role.Admin);
|
||||
await userManager.AddToRoleAsync(InitialData.Users.First(), IdentityConstant.Role.Admin);
|
||||
|
||||
await eventDispatcher.SendAsync(new UserCreated(InitialData.Users.First().Id, InitialData.Users.First().FirstName + " " + InitialData.Users.First().LastName, InitialData.Users.First().PassPortNumber));
|
||||
}
|
||||
@ -54,7 +55,7 @@ public class IdentityTestDataSeeder(
|
||||
|
||||
if (result.Succeeded)
|
||||
{
|
||||
await userManager.AddToRoleAsync(InitialData.Users.Last(), Constants.Role.User);
|
||||
await userManager.AddToRoleAsync(InitialData.Users.Last(), IdentityConstant.Role.User);
|
||||
|
||||
await eventDispatcher.SendAsync(new UserCreated(InitialData.Users.Last().Id, InitialData.Users.Last().FirstName + " " + InitialData.Users.Last().LastName, InitialData.Users.Last().PassPortNumber));
|
||||
}
|
||||
|
||||
@ -10,9 +10,7 @@
|
||||
},
|
||||
"Jwt": {
|
||||
"Authority": "http://identity:80",
|
||||
"Audience": "passenger-api",
|
||||
"RequireHttpsMetadata": false,
|
||||
"MetadataAddress": "http://identity:80/.well-known/openid-configuration"
|
||||
"Audience": "passenger-api"
|
||||
},
|
||||
"MongoOptions": {
|
||||
"ConnectionString": "mongodb://mongo:27017",
|
||||
|
||||
@ -11,9 +11,7 @@
|
||||
},
|
||||
"Jwt": {
|
||||
"Authority": "http://localhost:6005",
|
||||
"Audience": "passenger-api",
|
||||
"RequireHttpsMetadata": false,
|
||||
"MetadataAddress": "http://localhost:6005/.well-known/openid-configuration"
|
||||
"Audience": "passenger-api"
|
||||
},
|
||||
"RabbitMqOptions": {
|
||||
"HostName": "localhost",
|
||||
|
||||
10
building-blocks/Constants/IdentityConstant.cs
Normal file
10
building-blocks/Constants/IdentityConstant.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace BuildingBlocks.Constants;
|
||||
|
||||
public static class IdentityConstant
|
||||
{
|
||||
public static class Role
|
||||
{
|
||||
public const string Admin = "admin";
|
||||
public const string User = "user";
|
||||
}
|
||||
}
|
||||
@ -1,54 +1,48 @@
|
||||
using BuildingBlocks.Constants;
|
||||
using BuildingBlocks.Web;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace BuildingBlocks.Jwt;
|
||||
|
||||
using Duende.IdentityServer.EntityFramework.Entities;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
|
||||
public static class JwtExtensions
|
||||
namespace BuildingBlocks.Jwt
|
||||
{
|
||||
public static IServiceCollection AddJwt(this IServiceCollection services)
|
||||
public static class JwtExtensions
|
||||
{
|
||||
var jwtOptions = services.GetOptions<JwtBearerOptions>("Jwt");
|
||||
|
||||
services.AddAuthentication(
|
||||
o =>
|
||||
{
|
||||
o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
})
|
||||
.AddCookie(cfg => cfg.SlidingExpiration = true)
|
||||
.AddJwtBearer(
|
||||
JwtBearerDefaults.AuthenticationScheme,
|
||||
options =>
|
||||
{
|
||||
options.Authority = jwtOptions.Authority;
|
||||
|
||||
options.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateAudience = false,
|
||||
ClockSkew = TimeSpan.FromSeconds(2), // For prevent add default value (5min) to life time token!
|
||||
ValidateLifetime = true, // Enforce token expiry
|
||||
};
|
||||
|
||||
options.RequireHttpsMetadata = jwtOptions.RequireHttpsMetadata;
|
||||
options.MetadataAddress = jwtOptions.MetadataAddress;
|
||||
});
|
||||
|
||||
if (!string.IsNullOrEmpty(jwtOptions.Audience))
|
||||
public static IServiceCollection AddJwt(this IServiceCollection services)
|
||||
{
|
||||
// Bind Jwt settings from configuration
|
||||
var jwtOptions = services.GetOptions<JwtBearerOptions>("Jwt");
|
||||
|
||||
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||
.AddJwtBearer(options =>
|
||||
{
|
||||
options.Authority = jwtOptions.Authority;
|
||||
options.Audience = jwtOptions.Audience;
|
||||
options.RequireHttpsMetadata = true;
|
||||
|
||||
options.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuer = true,
|
||||
ValidIssuers = [jwtOptions.Authority],
|
||||
ValidateAudience = true,
|
||||
ValidAudiences = [jwtOptions.Audience],
|
||||
ValidateLifetime = true,
|
||||
ClockSkew = TimeSpan.FromSeconds(2), // Reduce default clock skew
|
||||
// For IdentityServer4/Duende, we should also validate the signing key
|
||||
ValidateIssuerSigningKey = true,
|
||||
NameClaimType = "name", // Map "name" claim to User.Identity.Name
|
||||
RoleClaimType = "role", // Map "role" claim to User.IsInRole()
|
||||
};
|
||||
|
||||
// Preserve ALL claims from the token (including "sub")
|
||||
options.MapInboundClaims = false;
|
||||
});
|
||||
|
||||
|
||||
services.AddAuthorization(
|
||||
options =>
|
||||
{
|
||||
// Set JWT as the default scheme for all [Authorize] attributes
|
||||
options.DefaultPolicy =
|
||||
new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme)
|
||||
.RequireAuthenticatedUser()
|
||||
.Build();
|
||||
|
||||
options.AddPolicy(
|
||||
nameof(ApiScope),
|
||||
policy =>
|
||||
@ -57,9 +51,27 @@ public static class JwtExtensions
|
||||
policy.RequireAuthenticatedUser();
|
||||
policy.RequireClaim("scope", jwtOptions.Audience);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return services;
|
||||
// Role-based policies
|
||||
options.AddPolicy(
|
||||
IdentityConstant.Role.Admin,
|
||||
x =>
|
||||
{
|
||||
x.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);
|
||||
x.RequireRole(IdentityConstant.Role.Admin);
|
||||
}
|
||||
);
|
||||
options.AddPolicy(
|
||||
IdentityConstant.Role.User,
|
||||
x =>
|
||||
{
|
||||
x.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);
|
||||
x.RequireRole(IdentityConstant.Role.User);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user