I am building authentication micro-service/domain by using DDD and I am still having trouble with identifying where does each service belong. At this point I am not sure does Authentication service belongs to the domain services or application services.
Should I wrap this behavior in domain serrvice, and expose response object via application service, or this should stay as it is - as application service.
public class AuthenticationService : IAuthenticationService
{
IAuthUnitOfWork _uow;
IUserRepository _userRepository;
IUserTokenFactory _userTokenFactory;
public AuthenticationService(IUserTokenFactory userTokenFactory, IUserRepository userRepository,
IAuthUnitOfWork uow)
{
_userTokenFactory = userTokenFactory;
_userRepository = userRepository;
_uow = uow;
}
public async Task<UserTokenResponse> AuthenticateAsync(string email, string password)
{
var user = await _userRepository.GetByEmailAndPasswordAsync(email, password);
//TODO: Add null check for user
var userToken = await _userTokenFactory.CreateWithAsync(user);
await _uow.SaveChangesAsync();
return new UserTokenResponse
{
ExpiressOn = userToken.ExpiressOn,
Token = userToken.Token
};
}
}
Application Services coordinate application flow and infrastructure, but do not execute business logic rules or invariants. It is common to see calls to repositories, units of work, and to accept and return service contract objects or request/response objects. They generally do not accept or return domain entities or valueobjects.
Domain services are unaware of infrastructure or overall application flow - they exclusively encapsulate business logic rules. They accept domain entities or value objects, carry out conditional operations on those entities or objects, or perform business rule calculations, and then return primitives or domain entities or value objects.
Based on these concepts, your sample service is definitely an application service, as it is interacting with your repository and unit of work, and returning a "UserResponse" type (a 'response' type does not sound like a domain entity).
Your application service AuthenticationService is delegating to a service called UserTokenFactory. UserTokenFactory accepts a domain entity (user) and returns a domain valueobject (usertoken). Presumably it encapsulates in an infrastructure-agnostic way the business rules associated with creating the user token. As such, this looks like more like a domain service. A factory which is responsible for the creation of domain concepts such as entities and value objects is just a special type of domain service (in my opinion) although you will most commonly see 'domain services' referring to services that perform some business logic that requires coordinating between multiple types of entities.
So - I think your structure here is appropriate - you have an application service coordinating infrastructure and flow, which delegates to a special service to execute the business logic.
Related
I am designing a REST backend in Nest.js that needs to allow Users to be a part of multiple Organizations. I want to use role-based access control, such that a user can have one or more named roles. Crucially, these roles need to be able to be either "global" (not dependent on any organization, ex. SUPERUSER), or "scoped" (specific to an organization, ex. MANAGER).
I have decided on this basic database design, which links Users to Organizations using the Roles table in a many-one-many relationship:
As you can see, the organizationId field on a Role is optional, and if it is present, then the user is linked to that organization through the role. If it is not present, I assume this to be a "global" role. I find this to be an elegant database design, but I am having trouble implementing the guard logic for my endpoints.
The guard logic would go something like this:
Look up all the Roles from the database that match the current userId.
For global routes, check that at least one of the returned roles is in the list of required roles for the route.
For scoped routes, do the same, but also check that the organizationId of the role matches the organization ID associated with the operation (I'll elaborate below).
Consider these two endpoints for Jobs. The first will retrieve all the jobs associated with a specified organization. The second will find a single job by its id:
Example route 1:
GET /jobs?organizationId=XXXXX
#Roles(Role.MANAGER, Role.EMPLOYEE)
#UseGuards(JwtAuthGuard, RolesGuard)
#Get()
getMyJobs(#Query() query: {organizationId: string}) {
return this.jobsService.getJobs({
organizationId: query.organizationId,
})
}
Example route 2:
GET /jobs/:jobId
#Roles(Role.MANAGER, Role.EMPLOYEE)
#UseGuards(JwtAuthGuard, RolesGuard)
#Get(':jobId')
getJob(#Param('jobId') jobId: string) {
return this.jobsService.getJob(jobId)
}
In the first example, I know the organizationId without doing any work because it is required as a query parameter. This id can be matched against the id specified in the Role. This is trivial to validate, and ensures that only users who belong to that organization can access the endpoint.
In the second example, the organizationId is not provided. I can easily query it from the database by looking up the Job, but that is work that should be done in the service/business logic. Additionally, guard logic executes before getJob. This is where I am stuck.
The only solution I can come up with is to pass the organizationId in every request, perhaps as a url parameter or HTTP header. Seems like there should be a better option than that. I'm sure this pattern is very common, but I don't know what it is called to do any research. Any help regarding this implementation would be greatly appreciated!
It is just another option for you.
You can modify a user object inside RolesGuard by adding a field that stores available organizations for him/her. So you need to calculate organizations for user, who makes a request inside a guard and then put a result array with ids of organizations to a user field (user.availableOrganizationIds = []). And then use it for filtering results
#Roles(Role.MANAGER, Role.EMPLOYEE)
#UseGuards(JwtAuthGuard, RolesGuard)
#Get()
getMyJobs(#User() user) { // get a user from request
return this.jobsService.getJobs({
organizationIds: user.availableOrganizationIds, // <<- filter by organizations
})
}
We are in the process of redesigning few our REST API endpoints to transition to a micro service architecture.
Here we are working on the endpoint /invitations/:id/confirm.
This endpoint creates a User, Account using the provided Invitation.
We have 3 aggregates Invitation, User and Account.
The nominal flow we are currently following is:
Check if Invitation exists
Make sure the invitation can be confirmed
Create User
Create Account
Delete Invitation
Return UserId
This operation is done in-process which explained why we can return a UserId right away. We simply load our aggregates from the db, perform the associated business logic and persist the result.
Introducing micro services will require asynchronous processing. In other words, we should send a command to a bus and return status code 202.
In our plan, we want to fire a command named RequestInvitationConfirmation. Basic validation will occur while instantiating this command.
Then this command will be sent through the bus to a consumer in charge of:
- Loading the invitation aggregates (make sure it exists)
- Calling the RequestConfirmation methods (will check that the invitation can be confirmed)
- Raising the InvitationConfirmationRequested event
The InvitationConfirmationRequested event will trigger a SAGA in charge of orchestrating the cross services communication
OnInvitationConfirmationRequested
Send CreateUser command
OnUserCreated
Send CreateAccount command
OnAccountCreated
Send DeleteInvitation command
OnInvitationDeleted
Raise InvitationConfirmed
Since it's asynchronous we need to provide a way to get the current operation state. I saw (https://www.adayinthelifeof.nl/2011/06/02/asynchronous-operations-in-rest/, https://asyncrestapi.docs.apiary.io/#) that a common approach
is to offer a /queue/:id OR /actions/:id endpoints.
This is where we get confused. How can you offer a single endpoint when states may be totally different from a SAGA to another?
Thx
For your saga to process messages within the scope of a single flow, you must correlate all your messages with the proper instance. When a saga is started by the first message, the saga identity is generated according to the rules:
Event(() => ItemAdded, x => x.CorrelateBy(cart => cart.UserName, context => context.Message.UserName)
.SelectId(context => Guid.NewGuid()));
So this id will be used as the identity of your saga that is persisted to the saga repository.
class ShoppingCart :
SagaStateMachineInstance
{
public Guid CorrelationId { get; set; }
public string CurrentState { get; set; }
Here, the CorrelationId is the saga id, therefore is the correlation id of the whole process.
If you have access to your saga repository (and you do), it is quite easy to expose an HTTP API endpoint to retrieve the current saga state by looking at the value of the CurrentState property in your saga state in the database that you use to persist sagas.
We're developing an application that uses a back-end built on .Net Core 2.2 Web API. Most of our controllers merely require the [Authorize] attribute with no policy specified. However, some endpoints are going to require the user to be in a particular Azure AD Security Group. For those cases, I implemented policies like this in the Startup.cs file:
var name = "PolicyNameIndicatingGroup";
var id = Guid.NewGuid; // Actually, this is set to the object ID of the group in AD.
services.AddAuthorization(
options =>
{
options.AddPolicy(
name,
policyBuilder => policyBuilder.RequireClaim(
"groups",
id.ToString()));
});
Then, on controllers requiring this type of authorization, I have:
[Authorize("PolicyNameIndicatingGroup")]
public async Task<ResponseBase<string>> GroupProtectedControllerMethod() {}
The problem is that our users are all in a large number of groups. This causes the Graph API to return no group claims at all, and instead a simple hasGroups boolean claim set to true. Therefore, no one has any groups, and thus cannot pass authorization. This no-groups issue can be read about here.
This string-based policy registration, lackluster as it may be, seems to be what the .Net Core people are recommending, yet it falls flat if the groups aren't populated on the User Claims. I'm not really seeing how to circumnavigate the issue. Is there some special way to set up the AppRegistration for my API so that it does get all of the groups populated on the User Claims?
Update:
In the solution, I do have a service that calls Graph to get the user's groups. However, I can't figure out how to call it before it's too late. In other words, when the user hits the AuthorizeAttribute on the controller to check for the policy, the user's groups have not yet been populated, so the protected method always blocks them with a 403.
My attempt consisted of making a custom base controller for all of my Web API Controllers. Within the base controller's constructor, I'm calling a method that checks the User.Identity (of type ClaimsIdentity) to see if it's been created and authenticated, and, if so, I'm using the ClaimsIdentity.AddClaim(Claim claim) method to populate the user's groups, as retrieved from my Graph call. However, when entering the base controller's constructor, the User.Identity hasn't been set up yet, so the groups don't get populated, as previously described. Somehow, I need the user's groups to be populated before I ever get to constructing the controller.
I found an answer to this solution thanks to some tips from someone on the ASP.NET Core team. This solution involves implementing an IClaimsTransformation (in the Microsoft.AspNetCore.Authentication namespace). To quote my source:
[IClaimsTransformation] is a service you wire into the request pipeline which will run after every authentication and you can use it to augment the identity as you like. That would be where you’d do your Graph API call [...]."
So I wrote the following implementation (see an important caveat below the code):
public class AdGroupClaimsTransformer : IClaimsTransformation
{
private const string AdGroupsAddedClaimType = "adGroupsAlreadyAdded";
private const string ObjectIdClaimType = "http://schemas.microsoft.com/identity/claims/objectidentifier";
private readonly IGraphService _graphService; // My service for querying Graph
private readonly ISecurityService _securityService; // My service for querying custom security information for the application
public AdGroupClaimsTransformer(IGraphService graphService, ISecurityService securityService)
{
_graphService = graphService;
_securityService = securityService;
}
public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
var claimsIdentity = principal.Identity as ClaimsIdentity;
var userIdentifier = FindClaimByType(claimsIdentity, ObjectIdClaimType);
var alreadyAdded = AdGroupsAlreadyAdded(claimsIdentity);
if (claimsIdentity == null || userIdentifier == null || alreadyAdded)
{
return Task.FromResult(principal);
}
var userSecurityGroups = _graphService.GetSecurityGroupsByUserId(userIdentifier).Result;
var allSecurityGroupModels = _securityService.GetSecurityGroups().Result.ToList();
foreach (var group in userSecurityGroups)
{
var groupIdentifier = allSecurityGroupModels.Single(m => m.GroupName == group).GroupGuid.ToString();
claimsIdentity.AddClaim(new Claim("groups", groupIdentifier));
}
claimsIdentity.AddClaim(new Claim(AdGroupsAddedClaimType, "true"));
return Task.FromResult(principal);
}
private static string FindClaimByType(ClaimsIdentity claimsIdentity, string claimType)
{
return claimsIdentity?.Claims?.FirstOrDefault(c => c.Type.Equals(claimType, StringComparison.Ordinal))
?.Value;
}
private static bool AdGroupsAlreadyAdded(ClaimsIdentity claimsIdentity)
{
var alreadyAdded = FindClaimByType(claimsIdentity, AdGroupsAddedClaimType);
var parsedSucceeded = bool.TryParse(alreadyAdded, out var valueWasTrue);
return parsedSucceeded && valueWasTrue;
}
}
Within my Startup.cs, in the ConfigureServices method, I register the implementation like this:
services.AddTransient<IClaimsTransformation, AdGroupClaimsTransformer>();
The Caveat
You may have noticed that my implementation is written defensively to make sure the transformation will not be run a second time on a ClaimsPrincipal that has already undergone the procedure. The potential issue here is that calls to the IClaimsTransformation might occur multiple times, and that might be bad in some scenarios. You can read more about this here.
You can use the Microsoft Graph API to query the user's groups instead:
POST https://graph.microsoft.com/v1.0/directoryObjects/{object-id}/getMemberGroups
Content-type: application/json
{
"securityEnabledOnly": true
}
Reference: https://learn.microsoft.com/en-us/graph/api/directoryobject-getmembergroups?view=graph-rest-1.0&tabs=http
The scenario will be:
Your client app will acquire access token (A) for accessing your back-end Web API.
Your Web API application will acquire access token (B) for accessing the Microsoft Graph API with the access token (A) using OAuth 2.0 On-Behalf-Of flow. Access token (B) will be used to get the user's groups.
Web API validates the user's group using a policy (recommended) or custom attribute.
The protocol diagram and sample request are listed in this article using the Azure AD V2.0 Endpoint. This article is for the V1.0 endpoint. Here are code samples for .Net Core.
Simplified scenario:
One Azure Mobile App "ServiceManagement" in eu (including database)
Two Azure Mobile Apps, same code, one in EU "ServiceEU", one in US "ServiceUS" (each with their own seperate database)
Customer signs in to ServiceManagement, selects what region he wants his data to be hosted at. Customer buys service options -> payment record, selected options etc. are saved in ServiceManagement.
Now ServiceManagement connects to either ServiceEU or ServiceUS and and tells it to create the corresponding service plan for the customer. Beyond the times the customer buys service options there will be no communication between the services.
Are there any other options besides HttpClient to handle the communication between the Azure Mobile App "ServiceManagement" and "ServiceEU" or "ServiceUS"?
My current code seems to work but I couldn't find any resources/documentation for cases like this. So I'm not sure if there are better alternatives.
/// <summary>
/// Hosted in ServiceManagement
/// </summary>
/// <returns></returns>
[HttpGet]
[ActionName("completeRemote")]
[ResponseType(typeof(String))]
public async Task<string> completeRemote()
{
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("ZUMO-API-VERSION", "2.0.0");
//Calling completeAll from different Azure Mobile App "<url>"
var res = await client.GetAsync("https://<url>/api/Test/completeAll");
res.EnsureSuccessStatusCode();
var str = await res.Content.ReadAsStringAsync();
str = JsonConvert.DeserializeObject<String>(str);
//verify request completed
//Do something with str
return str;
}
Please keep in mind that this is a very simplified concept
Are there any other options besides HttpClient to handle the communication between the Azure Mobile App "ServiceManagement" and "ServiceEU" or "ServiceUS"?
As far as I know, the C# mobile app backend is a web api. Normally we will use httpclient to send the request to the web api application to get the result.
If you don't want to use the httpclient, you could use azure mobile client SDK.
It is easily to write codes.
You could install it from the Nuget Package.
Install-Package Microsoft.Azure.Mobile.Client
Here is the sample code, hope it gives you some tips:
Notice: It still will send the request to the mobile app backend.
public static async Task<string> GetApplicationToken()
{
var clientUri = $"https://your-mobile-app-id.azurewebsites.net";
var client = new MobileServiceClient(clientUri);
var response = await client.InvokeApiAsync<string>("/api/values", HttpMethod.Get, null);
return response;
}
You could make use of Azure Service Bus (or host your own service bus)
Depend on Azure Service Bus when you need highly reliable cloud
messaging service between applications and services, even when one or
more is offline.
about Enterprise service bus on wikipedia:
An enterprise service bus (ESB) implements a communication system
between mutually interacting software applications in a
service-oriented architecture (SOA).
...
As it implements a software architecture for distributed computing, it
therefore also implements a special variant of the more general
client-server model. Whereas in general any application using ESB can
behave as server or client in turns.
I have a following structure.
Resources can have multiple endpoints.
Example:
Resource tickets can be accessed on following endpoints:
/api/tickets
/api/agent/tickets
/api/group/5/tickets
/api/tickets/closed etc.
At first, this looks like aggregate, where Resource is AR, and endpoints are child entities.
I also have UserTypes.
What I need is to build a relation between Usertypes and Endpoints, so each UserType can have a diferrent access for endpoints.
Example for, UserType admin could access all endpoints for tickets resource, while user type agent could have access to only portion of endpoints for the same resource.
What would be a suggested way to connect EndPoints and UserTypes in terms of DDD?
Do you need anything else other than a collection of mapping a between Resources and Endpoints on a UserType? This would give you all usertypes their unique resource endpoint access rights
Also seems to be the same question as Solve apparent need for outside reference to entity inside aggregate (DDD)
I would probably create something like the following:
class ResourceEndpoint {
Guid resourceId;
Guid endpointId;
}
class UserType {
List<ResourceEndpoint> ThingsICanAccess;
}