Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
H
HIAST-Clinics
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
almohanad.hafez
HIAST-Clinics
Commits
2ea3eb22
Commit
2ea3eb22
authored
Aug 21, 2024
by
Almouhannad
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
(B) Added login service, JWT config, auth config
parent
fb8a91f1
Changes
16
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
271 additions
and
5 deletions
+271
-5
JWTBearerOptionsSetup.cs
Clinics.Backend/API/Options/JWT/JWTBearerOptionsSetup.cs
+27
-0
JWTOptionsSetup.cs
Clinics.Backend/API/Options/JWT/JWTOptionsSetup.cs
+22
-0
Program.cs
Clinics.Backend/API/Program.cs
+45
-2
appsettings.json
Clinics.Backend/API/appsettings.json
+5
-0
IJWTProvider.cs
Clinics.Backend/Application/Abstractions/JWT/IJWTProvider.cs
+8
-0
LoginCommand.cs
....Backend/Application/Users/Commands/Login/LoginCommand.cs
+9
-0
LoginCommandHandler.cs
...d/Application/Users/Commands/Login/LoginCommandHandler.cs
+44
-0
IdentityErrors.cs
Clinics.Backend/Domain/Errors/IdentityErrors.cs
+1
-0
IUserRepository.cs
Clinics.Backend/Domain/Repositories/IUserRepository.cs
+3
-0
JWTOptions.cs
...end/Persistence/Identity/Authentication/JWT/JWTOptions.cs
+9
-0
JWTProvider.cs
...nd/Persistence/Identity/Authentication/JWT/JWTProvider.cs
+47
-0
Persistence.csproj
Clinics.Backend/Persistence/Persistence.csproj
+1
-0
SpecificationEvaluator.cs
...tories/Specifications/Evaluator/SpecificationEvaluator.cs
+2
-1
UserRepository.cs
....Backend/Persistence/Repositories/Users/UserRepository.cs
+18
-1
EmployeesController.cs
...s.Backend/Presentation/Controllers/EmployeesController.cs
+2
-1
UsersController.cs
Clinics.Backend/Presentation/Controllers/UsersController.cs
+28
-0
No files found.
Clinics.Backend/API/Options/JWT/JWTBearerOptionsSetup.cs
0 → 100644
View file @
2ea3eb22
using
Microsoft.AspNetCore.Authentication.JwtBearer
;
using
Microsoft.Extensions.Options
;
using
Microsoft.IdentityModel.Tokens
;
using
Persistence.Identity.Authentication.JWT
;
using
System.Text
;
namespace
API.Options.JWT
;
public
class
JWTBearerOptionsSetup
:
IPostConfigureOptions
<
JwtBearerOptions
>
{
#
region
JWT
Options
CTOR
DI
private
readonly
JWTOptions
_jwtOptions
;
public
JWTBearerOptionsSetup
(
IOptions
<
JWTOptions
>
jwtOptions
)
{
_jwtOptions
=
jwtOptions
.
Value
;
}
#
endregion
public
void
PostConfigure
(
string
?
name
,
JwtBearerOptions
options
)
{
options
.
TokenValidationParameters
.
ValidIssuer
=
_jwtOptions
.
Issuer
;
options
.
TokenValidationParameters
.
ValidAudience
=
_jwtOptions
.
Audience
;
options
.
TokenValidationParameters
.
IssuerSigningKey
=
new
SymmetricSecurityKey
(
Encoding
.
UTF8
.
GetBytes
(
_jwtOptions
.
SecretKey
));
}
}
Clinics.Backend/API/Options/JWT/JWTOptionsSetup.cs
0 → 100644
View file @
2ea3eb22
using
Microsoft.Extensions.Options
;
using
Persistence.Identity.Authentication.JWT
;
namespace
API.Options.JWT
;
public
class
JWTOptionsSetup
:
IConfigureOptions
<
JWTOptions
>
{
private
const
string
_cofigurationSectionName
=
"JWTOptions"
;
// From appsettings.json
#
region
Using
ctor
DI
to
access
configuration
private
readonly
IConfiguration
_configuration
;
public
JWTOptionsSetup
(
IConfiguration
configuration
)
{
_configuration
=
configuration
;
}
#
endregion
public
void
Configure
(
JWTOptions
options
)
{
_configuration
.
GetSection
(
_cofigurationSectionName
).
Bind
(
options
);
}
}
Clinics.Backend/API/Program.cs
View file @
2ea3eb22
using
API.Options.Database
;
using
API.Options.JWT
;
using
API.SeedDatabaseHelper
;
using
Application.Behaviors
;
using
FluentValidation
;
using
MediatR
;
using
Microsoft.AspNetCore.Authentication.JwtBearer
;
using
Microsoft.EntityFrameworkCore
;
using
Microsoft.Extensions.Options
;
using
Microsoft.OpenApi.Models
;
using
Persistence.Context
;
var
builder
=
WebApplication
.
CreateBuilder
(
args
);
...
...
@@ -65,9 +68,47 @@ builder.Services.AddControllers()
.
AddApplicationPart
(
Presentation
.
AssemblyReference
.
Assembly
);
#endregion
builder
.
Services
.
AddEndpointsApiExplorer
();
builder
.
Services
.
AddSwaggerGen
();
#region Swagger with JWT authorization
builder
.
Services
.
AddSwaggerGen
(
opt
=>
{
opt
.
SwaggerDoc
(
"v1"
,
new
OpenApiInfo
{
Title
=
"MyAPI"
,
Version
=
"v1"
});
opt
.
AddSecurityDefinition
(
"Bearer"
,
new
OpenApiSecurityScheme
{
In
=
ParameterLocation
.
Header
,
Description
=
"Please enter token"
,
Name
=
"Authorization"
,
Type
=
SecuritySchemeType
.
Http
,
BearerFormat
=
"JWT"
,
Scheme
=
"bearer"
});
opt
.
AddSecurityRequirement
(
new
OpenApiSecurityRequirement
{
{
new
OpenApiSecurityScheme
{
Reference
=
new
OpenApiReference
{
Type
=
ReferenceType
.
SecurityScheme
,
Id
=
"Bearer"
}
},
new
string
[]{}
}
});
});
#endregion
#region Authentication options
builder
.
Services
.
ConfigureOptions
<
JWTOptionsSetup
>();
builder
.
Services
.
ConfigureOptions
<
JWTBearerOptionsSetup
>();
builder
.
Services
.
AddAuthentication
(
JwtBearerDefaults
.
AuthenticationScheme
)
.
AddJwtBearer
();
#endregion
var
app
=
builder
.
Build
();
...
...
@@ -85,6 +126,8 @@ if (app.Environment.IsDevelopment())
app
.
UseHttpsRedirection
();
app
.
UseAuthentication
();
app
.
UseAuthorization
();
app
.
MapControllers
();
...
...
Clinics.Backend/API/appsettings.json
View file @
2ea3eb22
...
...
@@ -15,5 +15,10 @@
"MaxRetryCount"
:
3
,
"CommandTimeout"
:
30
,
"EnableDetailedErrors"
:
true
},
"JWTOptions"
:
{
"SecretKey"
:
"MoHafez11@SecretKeyAPIVeryLongSecretKeyMoHafez11@SecretKeyAPIVeryLongSecretKey"
,
"Audience"
:
"clinics-front-end.hiast.edy.sy"
,
"Issuer"
:
"clinics-back-end.hiast.edy.sy"
}
}
Clinics.Backend/Application/Abstractions/JWT/IJWTProvider.cs
0 → 100644
View file @
2ea3eb22
using
Domain.Entities.Identity.Users
;
namespace
Application.Abstractions.JWT
;
public
interface
IJWTProvider
{
string
Generate
(
User
user
);
}
Clinics.Backend/Application/Users/Commands/Login/LoginCommand.cs
0 → 100644
View file @
2ea3eb22
using
Application.Abstractions.CQRS.Commands
;
namespace
Application.Users.Commands.Login
;
public
class
LoginCommand
:
ICommand
<
string
>
{
public
string
UserName
{
get
;
set
;
}
=
null
!;
public
string
Password
{
get
;
set
;
}
=
null
!;
}
Clinics.Backend/Application/Users/Commands/Login/LoginCommandHandler.cs
0 → 100644
View file @
2ea3eb22
using
Application.Abstractions.CQRS.Commands
;
using
Application.Abstractions.JWT
;
using
Domain.Entities.Identity.Users
;
using
Domain.Errors
;
using
Domain.Repositories
;
using
Domain.Shared
;
using
Domain.UnitOfWork
;
namespace
Application.Users.Commands.Login
;
public
class
LoginCommandHandler
:
CommandHandlerBase
<
LoginCommand
,
string
>
{
#
region
CTOR
DI
private
readonly
IUserRepository
_userRepository
;
private
readonly
IJWTProvider
_jwtProvider
;
public
LoginCommandHandler
(
IUnitOfWork
unitOfWork
,
IUserRepository
userRepository
,
IJWTProvider
jwtProvider
)
:
base
(
unitOfWork
)
{
_userRepository
=
userRepository
;
_jwtProvider
=
jwtProvider
;
}
#
endregion
public
override
async
Task
<
Result
<
string
>>
HandleHelper
(
LoginCommand
request
,
CancellationToken
cancellationToken
)
{
#
region
1.
Check
username
and
password
are
correct
Result
<
User
?>
loginResult
=
await
_userRepository
.
VerifyPasswordAsync
(
request
.
UserName
,
request
.
Password
);
if
(
loginResult
.
IsFailure
)
return
Result
.
Failure
<
string
>(
loginResult
.
Error
);
// Not found username
if
(
loginResult
.
Value
is
null
)
// Invalid password
return
Result
.
Failure
<
string
>(
IdentityErrors
.
PasswordMismatch
);
#
endregion
#
region
2.
Generate
JWT
User
user
=
loginResult
.
Value
!;
string
token
=
_jwtProvider
.
Generate
(
user
);
#
endregion
return
Result
.
Success
<
string
>(
token
);
}
}
Clinics.Backend/Domain/Errors/IdentityErrors.cs
View file @
2ea3eb22
...
...
@@ -6,4 +6,5 @@ public static class IdentityErrors
{
public
static
Error
InvalidRole
=>
new
(
"Identity.InvalidRole"
,
"Role specified for user is invalid"
);
public
static
Error
NotFound
=>
new
(
"Identity.NotFound"
,
"المستخدم غير مسجّل في النظام"
);
public
static
Error
PasswordMismatch
=>
new
(
"Identity.PasswordMismatch"
,
"كلمة السر غير صحيحة"
);
}
Clinics.Backend/Domain/Repositories/IUserRepository.cs
View file @
2ea3eb22
...
...
@@ -6,5 +6,8 @@ namespace Domain.Repositories;
public
interface
IUserRepository
:
IRepository
<
User
>
{
public
Task
<
Result
<
User
>>
GetByUserNameFullAsync
(
string
userName
);
public
Task
<
Result
<
User
?>>
VerifyPasswordAsync
(
string
userName
,
string
password
);
}
Clinics.Backend/Persistence/Identity/Authentication/JWT/JWTOptions.cs
0 → 100644
View file @
2ea3eb22
namespace
Persistence.Identity.Authentication.JWT
;
// Using option pattern
public
class
JWTOptions
{
public
string
Issuer
{
get
;
init
;
}
=
null
!;
public
string
Audience
{
get
;
init
;
}
=
null
!;
public
string
SecretKey
{
get
;
init
;
}
=
null
!;
}
Clinics.Backend/Persistence/Identity/Authentication/JWT/JWTProvider.cs
0 → 100644
View file @
2ea3eb22
using
Application.Abstractions.JWT
;
using
Domain.Entities.Identity.Users
;
using
Microsoft.Extensions.Options
;
using
Microsoft.IdentityModel.Tokens
;
using
System.IdentityModel.Tokens.Jwt
;
using
System.Security.Claims
;
using
System.Text
;
namespace
Persistence.Identity.Authentication.JWT
;
public
sealed
class
JWTProvider
:
IJWTProvider
{
private
readonly
JWTOptions
_options
;
public
JWTProvider
(
IOptions
<
JWTOptions
>
options
)
{
_options
=
options
.
Value
;
}
public
string
Generate
(
User
user
)
{
var
claims
=
new
Claim
[]
{
new
(
JwtRegisteredClaimNames
.
Sub
,
user
.
Id
.
ToString
()),
new
(
JwtRegisteredClaimNames
.
UniqueName
,
user
.
UserName
),
};
var
signingCredentials
=
new
SigningCredentials
(
new
SymmetricSecurityKey
(
Encoding
.
UTF8
.
GetBytes
(
_options
.
SecretKey
)),
SecurityAlgorithms
.
HmacSha256
);
var
token
=
new
JwtSecurityToken
(
_options
.
Issuer
,
_options
.
Audience
,
claims
,
null
,
DateTime
.
UtcNow
.
AddDays
(
30
),
signingCredentials
);
var
tokenValue
=
new
JwtSecurityTokenHandler
()
.
WriteToken
(
token
);
return
tokenValue
;
}
}
Clinics.Backend/Persistence/Persistence.csproj
View file @
2ea3eb22
...
...
@@ -7,6 +7,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8">
<PrivateAssets>all</PrivateAssets>
...
...
Clinics.Backend/Persistence/Repositories/Specifications/Evaluator/SpecificationEvaluator.cs
View file @
2ea3eb22
...
...
@@ -18,7 +18,8 @@ public static class SpecificationEvaluator
queryable
=
queryable
.
Where
(
specification
.
Criteria
);
}
specification
.
IncludeExpressions
.
Aggregate
(
queryable
=
specification
.
IncludeExpressions
.
Aggregate
(
queryable
,
(
current
,
includeExpression
)
=>
current
.
Include
(
includeExpression
)
...
...
Clinics.Backend/Persistence/Repositories/Users/UserRepository.cs
View file @
2ea3eb22
...
...
@@ -4,6 +4,7 @@ using Domain.Repositories;
using
Domain.Shared
;
using
Microsoft.EntityFrameworkCore
;
using
Persistence.Context
;
using
Persistence.Identity.PasswordsHashing
;
using
Persistence.Repositories.Base
;
namespace
Persistence.Repositories.Users
;
...
...
@@ -11,8 +12,10 @@ namespace Persistence.Repositories.Users;
public
class
UserRepository
:
Repositroy
<
User
>,
IUserRepository
{
#
region
Ctor
DI
public
UserRepository
(
ClinicsDbContext
context
)
:
base
(
context
)
private
readonly
IPasswordHasher
_passwordHasher
;
public
UserRepository
(
ClinicsDbContext
context
,
IPasswordHasher
passwordHasher
)
:
base
(
context
)
{
_passwordHasher
=
passwordHasher
;
}
#
endregion
...
...
@@ -39,4 +42,18 @@ public class UserRepository : Repositroy<User>, IUserRepository
}
#
endregion
#
region
Verify
password
public
async
Task
<
Result
<
User
?>>
VerifyPasswordAsync
(
string
userName
,
string
password
)
{
var
userResult
=
await
GetByUserNameFullAsync
(
userName
);
if
(
userResult
.
IsFailure
)
return
Result
.
Failure
<
User
?>(
userResult
.
Error
);
if
(!
_passwordHasher
.
Verify
(
password
,
userResult
.
Value
.
HashedPassword
))
return
Result
.
Success
<
User
?>(
null
);
return
Result
.
Success
<
User
?>(
userResult
.
Value
);
}
#
endregion
}
Clinics.Backend/Presentation/Controllers/EmployeesController.cs
View file @
2ea3eb22
using
Application.Employees.Commands.AttachFamilyMemberToEmployee
;
using
Application.Employees.Commands.CreateEmployee
;
using
MediatR
;
using
Microsoft.AspNetCore.Authorization
;
using
Microsoft.AspNetCore.Mvc
;
using
Presentation.Controllers.Base
;
...
...
@@ -16,7 +17,7 @@ public class EmployeesController : ApiController
}
#
endregion
[
Authorize
]
[
HttpPost
]
public
async
Task
<
IActionResult
>
Create
([
FromBody
]
CreateEmployeeCommand
command
)
{
...
...
Clinics.Backend/Presentation/Controllers/UsersController.cs
0 → 100644
View file @
2ea3eb22
using
Application.Users.Commands.Login
;
using
MediatR
;
using
Microsoft.AspNetCore.Mvc
;
using
Presentation.Controllers.Base
;
namespace
Presentation.Controllers
;
[Route("api/Users")]
public
class
UsersController
:
ApiController
{
#
region
CTOR
DI
public
UsersController
(
ISender
sender
)
:
base
(
sender
)
{
}
#
endregion
[
HttpPost
]
public
async
Task
<
IActionResult
>
LoginUser
([
FromBody
]
LoginCommand
command
)
{
var
result
=
await
_sender
.
Send
(
command
);
if
(
result
.
IsFailure
)
return
HandleFailure
(
result
);
return
Ok
(
result
.
Value
);
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment