aboutsummaryrefslogtreecommitdiff
path: root/Timeline
diff options
context:
space:
mode:
Diffstat (limited to 'Timeline')
-rw-r--r--Timeline/ClientApp/src/app/user/internal-user-service/errors.ts16
-rw-r--r--Timeline/ClientApp/src/app/user/internal-user-service/http-entities.ts5
-rw-r--r--Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.spec.ts1
-rw-r--r--Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.ts29
-rw-r--r--Timeline/Controllers/UserController.cs49
-rw-r--r--Timeline/Entities/Token.cs26
-rw-r--r--Timeline/Services/JwtService.cs26
-rw-r--r--Timeline/Timeline.csproj4
8 files changed, 80 insertions, 76 deletions
diff --git a/Timeline/ClientApp/src/app/user/internal-user-service/errors.ts b/Timeline/ClientApp/src/app/user/internal-user-service/errors.ts
index 22e44dd6..3358a9d9 100644
--- a/Timeline/ClientApp/src/app/user/internal-user-service/errors.ts
+++ b/Timeline/ClientApp/src/app/user/internal-user-service/errors.ts
@@ -1,25 +1,29 @@
-export abstract class LoginError extends Error { }
-
-export class BadNetworkError extends LoginError {
+export class BadNetworkError extends Error {
constructor() {
super('Network is bad.');
}
}
-export class AlreadyLoginError extends LoginError {
+export class AlreadyLoginError extends Error {
constructor() {
super('Internal logical error. There is already a token saved. Please call validateUserLoginState first.');
}
}
-export class BadCredentialsError extends LoginError {
+export class BadCredentialsError extends Error {
constructor() {
super('Username or password is wrong.');
}
}
-export class UnknownError extends LoginError {
+export class UnknownError extends Error {
constructor(public internalError?: any) {
super('Sorry, unknown error occured!');
}
}
+
+export class ServerInternalError extends Error {
+ constructor(message?: string) {
+ super('Wrong server response. ' + message);
+ }
+}
diff --git a/Timeline/ClientApp/src/app/user/internal-user-service/http-entities.ts b/Timeline/ClientApp/src/app/user/internal-user-service/http-entities.ts
index 5664cf7c..f52233c9 100644
--- a/Timeline/ClientApp/src/app/user/internal-user-service/http-entities.ts
+++ b/Timeline/ClientApp/src/app/user/internal-user-service/http-entities.ts
@@ -6,8 +6,9 @@ export const validateTokenUrl = '/api/User/ValidateToken';
export type CreateTokenRequest = UserCredentials;
export interface CreateTokenResponse {
- token: string;
- userInfo: UserInfo;
+ success: boolean;
+ token?: string;
+ userInfo?: UserInfo;
}
export interface ValidateTokenRequest {
diff --git a/Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.spec.ts b/Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.spec.ts
index 6906ed60..15755382 100644
--- a/Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.spec.ts
+++ b/Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.spec.ts
@@ -102,6 +102,7 @@ describe('InternalUserService', () => {
request.url === createTokenUrl && request.body !== null &&
request.body.username === mockUserCredentials.username &&
request.body.password === mockUserCredentials.password).flush(<CreateTokenResponse>{
+ success: true,
token: mockToken,
userInfo: mockUserInfo
});
diff --git a/Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.ts b/Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.ts
index 6de355f2..66eafde9 100644
--- a/Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.ts
+++ b/Timeline/ClientApp/src/app/user/internal-user-service/internal-user.service.ts
@@ -5,7 +5,7 @@ import { Router } from '@angular/router';
import { Observable, throwError, BehaviorSubject, of } from 'rxjs';
import { map, catchError, retry, switchMap, tap, filter } from 'rxjs/operators';
-import { AlreadyLoginError, BadCredentialsError, BadNetworkError, UnknownError } from './errors';
+import { AlreadyLoginError, BadCredentialsError, BadNetworkError, UnknownError, ServerInternalError } from './errors';
import {
createTokenUrl, validateTokenUrl, CreateTokenRequest,
CreateTokenResponse, ValidateTokenRequest, ValidateTokenResponse
@@ -84,7 +84,7 @@ export class InternalUserService {
if (userInfo) {
return of(userInfo);
} else {
- return throwError(new Error('Wrong server response. IsValid is true but UserInfo is null.'));
+ return throwError(new ServerInternalError('IsValid is true but UserInfo is null.'));
}
} else {
return of(null);
@@ -117,21 +117,28 @@ export class InternalUserService {
if (error.error instanceof ErrorEvent) {
console.error('An error occurred when login: ' + error.error.message);
return throwError(new BadNetworkError());
- } else if (error.status === 400) {
- console.error('An error occurred when login: wrong credentials.');
- return throwError(new BadCredentialsError());
} else {
console.error('An unknown error occurred when login: ' + error);
return throwError(new UnknownError(error));
}
}),
- map(result => {
- this.token = result.token;
- if (info.rememberMe) {
- this.window.localStorage.setItem(TOKEN_STORAGE_KEY, result.token);
+ switchMap(result => {
+ if (result.success) {
+ if (result.token && result.userInfo) {
+ this.token = result.token;
+ if (info.rememberMe) {
+ this.window.localStorage.setItem(TOKEN_STORAGE_KEY, result.token);
+ }
+ this.userInfoSubject.next(result.userInfo);
+ return of(result.userInfo);
+ } else {
+ console.error('An error occurred when login: server return wrong data.');
+ return throwError(new ServerInternalError('Token or userInfo is null.'));
+ }
+ } else {
+ console.error('An error occurred when login: wrong credentials.');
+ return throwError(new BadCredentialsError());
}
- this.userInfoSubject.next(result.userInfo);
- return result.userInfo;
})
);
}
diff --git a/Timeline/Controllers/UserController.cs b/Timeline/Controllers/UserController.cs
index 45242ce3..eb1b8513 100644
--- a/Timeline/Controllers/UserController.cs
+++ b/Timeline/Controllers/UserController.cs
@@ -15,23 +15,6 @@ namespace Timeline.Controllers
public const int LogInFailed = 4001;
}
- public class UserCredentials
- {
- public string Username { get; set; }
- public string Password { get; set; }
- }
-
- public class CreateTokenResult
- {
- public string Token { get; set; }
- public UserInfo UserInfo { get; set; }
- }
-
- public class TokenValidationRequest
- {
- public string Token { get; set; }
- }
-
private readonly IUserService _userService;
private readonly IJwtService _jwtService;
private readonly ILogger<UserController> _logger;
@@ -45,39 +28,31 @@ namespace Timeline.Controllers
[HttpPost("[action]")]
[AllowAnonymous]
- public ActionResult<CreateTokenResult> CreateToken([FromBody] UserCredentials credentials)
+ public ActionResult<CreateTokenResponse> CreateToken([FromBody] CreateTokenRequest request)
{
- var user = _userService.Authenticate(credentials.Username, credentials.Password);
+ var user = _userService.Authenticate(request.Username, request.Password);
if (user == null) {
- _logger.LogInformation(LoggingEventIds.LogInFailed, "Attemp to login with username: {} and password: {} failed.", credentials.Username, credentials.Password);
- return BadRequest();
+ _logger.LogInformation(LoggingEventIds.LogInFailed, "Attemp to login with username: {} and password: {} failed.", request.Username, request.Password);
+ return Ok(new CreateTokenResponse
+ {
+ Success = false
+ });
}
- _logger.LogInformation(LoggingEventIds.LogInSucceeded, "Login with username: {} succeeded.", credentials.Username);
+ _logger.LogInformation(LoggingEventIds.LogInSucceeded, "Login with username: {} succeeded.", request.Username);
- var result = new CreateTokenResult
+ return Ok(new CreateTokenResponse
{
+ Success = true,
Token = _jwtService.GenerateJwtToken(user),
UserInfo = user.GetUserInfo()
- };
-
- return Ok(result);
- }
-
- [HttpPost("[action]")]
- [Consumes("text/plain")]
- [AllowAnonymous]
- public ActionResult<TokenValidationResult> ValidateToken([FromBody] string token)
- {
- var result = _jwtService.ValidateJwtToken(token);
- return Ok(result);
+ });
}
[HttpPost("[action]")]
- [Consumes("application/json")]
[AllowAnonymous]
- public ActionResult<TokenValidationResult> ValidateToken([FromBody] TokenValidationRequest request)
+ public ActionResult<TokenValidationResponse> ValidateToken([FromBody] TokenValidationRequest request)
{
var result = _jwtService.ValidateJwtToken(request.Token);
return Ok(result);
diff --git a/Timeline/Entities/Token.cs b/Timeline/Entities/Token.cs
new file mode 100644
index 00000000..ce5b92ff
--- /dev/null
+++ b/Timeline/Entities/Token.cs
@@ -0,0 +1,26 @@
+namespace Timeline.Entities
+{
+ public class CreateTokenRequest
+ {
+ public string Username { get; set; }
+ public string Password { get; set; }
+ }
+
+ public class CreateTokenResponse
+ {
+ public bool Success { get; set; }
+ public string Token { get; set; }
+ public UserInfo UserInfo { get; set; }
+ }
+
+ public class TokenValidationRequest
+ {
+ public string Token { get; set; }
+ }
+
+ public class TokenValidationResponse
+ {
+ public bool IsValid { get; set; }
+ public UserInfo UserInfo { get; set; }
+ }
+}
diff --git a/Timeline/Services/JwtService.cs b/Timeline/Services/JwtService.cs
index a01f3f2b..abdde908 100644
--- a/Timeline/Services/JwtService.cs
+++ b/Timeline/Services/JwtService.cs
@@ -11,12 +11,6 @@ using Timeline.Entities;
namespace Timeline.Services
{
- public class TokenValidationResult
- {
- public bool IsValid { get; set; }
- public UserInfo UserInfo { get; set; }
- }
-
public interface IJwtService
{
/// <summary>
@@ -30,17 +24,17 @@ namespace Timeline.Services
/// <summary>
/// Validate a JWT token.
/// Return null is <paramref name="token"/> is null.
- /// If token is invalid, return a <see cref="TokenValidationResult"/> with
- /// <see cref="TokenValidationResult.IsValid"/> set to false and
- /// <see cref="TokenValidationResult.UserInfo"/> set to null.
- /// If token is valid, return a <see cref="TokenValidationResult"/> with
- /// <see cref="TokenValidationResult.IsValid"/> set to true and
- /// <see cref="TokenValidationResult.UserInfo"/> filled with the user info
+ /// If token is invalid, return a <see cref="TokenValidationResponse"/> with
+ /// <see cref="TokenValidationResponse.IsValid"/> set to false and
+ /// <see cref="TokenValidationResponse.UserInfo"/> set to null.
+ /// If token is valid, return a <see cref="TokenValidationResponse"/> with
+ /// <see cref="TokenValidationResponse.IsValid"/> set to true and
+ /// <see cref="TokenValidationResponse.UserInfo"/> filled with the user info
/// in the token.
/// </summary>
/// <param name="token">The token string to validate.</param>
/// <returns>Null if <paramref name="token"/> is null. Or the result.</returns>
- TokenValidationResult ValidateJwtToken(string token);
+ TokenValidationResponse ValidateJwtToken(string token);
}
@@ -86,7 +80,7 @@ namespace Timeline.Services
}
- public TokenValidationResult ValidateJwtToken(string token)
+ public TokenValidationResponse ValidateJwtToken(string token)
{
if (token == null)
return null;
@@ -114,7 +108,7 @@ namespace Timeline.Services
Roles = identity.FindAll(identity.RoleClaimType).Select(claim => claim.Value).ToArray()
};
- return new TokenValidationResult
+ return new TokenValidationResponse
{
IsValid = true,
UserInfo = userInfo
@@ -123,7 +117,7 @@ namespace Timeline.Services
catch (Exception e)
{
_logger.LogInformation(e, "Token validation failed! Token is {} .", token);
- return new TokenValidationResult { IsValid = false };
+ return new TokenValidationResponse { IsValid = false };
}
}
}
diff --git a/Timeline/Timeline.csproj b/Timeline/Timeline.csproj
index 330d2981..e55eb90d 100644
--- a/Timeline/Timeline.csproj
+++ b/Timeline/Timeline.csproj
@@ -27,10 +27,6 @@
<None Include="$(SpaRoot)**" Exclude="$(SpaRoot)node_modules\**" />
</ItemGroup>
- <ItemGroup>
- <Folder Include="bin\" />
- </ItemGroup>
-
<Target Name="DebugEnsureNodeEnv" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('$(SpaRoot)node_modules') ">
<!-- Ensure Node.js is installed -->
<Exec Command="node --version" ContinueOnError="true">