Skip to content

Commit

Permalink
Setup login specification
Browse files Browse the repository at this point in the history
  • Loading branch information
jrsmth-tier2 committed May 3, 2024
1 parent 8d6495d commit 24d8632
Show file tree
Hide file tree
Showing 8 changed files with 73 additions and 31 deletions.
40 changes: 32 additions & 8 deletions cypress/e2e/auth/login.spec.cy.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,32 @@
/** What authentication rules should the webapp have? */
/**
* Login
* Should sign user in, then route to /home
* Failed login should present the reason and give user another chance (unlimited)
*/

// Question :: is it possible to get a list of test descriptions to document what this feature does

describe('Login', () => {
const homePage = '/home';
const loginPage = '/auth/login';
const loginUrl = 'http://localhost:8010/auth/login';

// Question :: is it possible to get a list of test descriptions to document what this feature does

beforeEach(() => {
cy.intercept('POST', loginUrl, { fixture: 'auth/unsigned-token-with-max-expiry' });

cy.visit(loginPage);
});

it('should sign user in when valid credentials are used', () => {
/** When: */
cy.get('#username').type('[email protected]');
cy.get('#password').type('password');
cy.get('#login-btn').click();

/** Then: user is routed to home page */
cy.location('pathname').should('eq', homePage);

});

it('should reject login when invalid credentials are used', () => {
// TODO

/** Then: user is presented with failure reason and given another change to login */
});

});
2 changes: 2 additions & 0 deletions cypress/e2e/auth/route.spec.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ describe('Protected Routes', () => {
"auth/register"
];

// Note :: end to end test: no setting storage directly, login if you have to...

beforeEach(() => {
cy.visit(redirect);
});
Expand Down
3 changes: 3 additions & 0 deletions cypress/fixtures/auth/unsigned-token-with-max-expiry.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjk5OTk5OTk5OTk5OX0.RAXPOm19cHn5mG7fSyo1K6wamKUX8XXzKsLKpz_Lb3I"
}
2 changes: 1 addition & 1 deletion src/app/core/interceptor/error.interceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { inject } from "@angular/core";
import { AuthenticationService } from "../service/auth/authentication.service";

/**
* Http Interceptor that triggers on errored requests
* Http Interceptor that handles response errors
*
* @author J. R. Smith
* @since 1st May 2024
Expand Down
20 changes: 13 additions & 7 deletions src/app/core/service/auth/authentication.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,21 @@ const URI = environment.services.security.uri;
})
export class AuthenticationService {

constructor(private http: HttpService, private token: TokenService, private router: Router) { }
constructor(
private http: HttpService,
private token: TokenService,
private router: Router) { }

/** Attempt to sign in a user to Cardinal with given credentials */
async login() {
this.http.post(BASE_URL, URI.auth.login, '');

this.token.set('a_token')

// Note :: then redirect to /home
async login(credentials: any) {
await this.http.post(BASE_URL, URI.auth.login, credentials).subscribe(
(res: any) => {
const token = res.token;
this.token.set(token);
}
);

this.router.navigate(['/home']).then();
}

/** Attempt to register a new Cardinal user */
Expand Down
25 changes: 13 additions & 12 deletions src/app/core/service/auth/token.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { computed, Injectable, Signal, signal, WritableSignal } from '@angular/core';
import { Injectable } from '@angular/core';
import { Token } from "../../model/token";
import { jwtDecode } from "jwt-decode";
import { environment } from "../../../../environments/environment";
Expand All @@ -14,14 +14,11 @@ const URI = environment.services.security.uri;
})
export class TokenService {

private token: WritableSignal<null | string> = signal(null);
private decoded: Signal<null | Token> = computed(() => this.decode(this.token()));

constructor(private http: HttpService, private storage: StorageService) { }

/** Retrieve authentication token */
get(): string {
const token = this.token();
const token = this.storage.get('token');
const empty = !token;

return empty ? '' : token as string;
Expand All @@ -34,9 +31,7 @@ export class TokenService {

/** Determine if user is signed in by presence of valid token */
async hasValidToken(): Promise<boolean> {
this.token.update(() => this.storage.get('token'));

const tokenEmpty = !this.decoded();
const tokenEmpty = !this.get();
if (tokenEmpty) return false;

return this.hasValidExpiry() && await this.hasValidSignature();
Expand All @@ -49,7 +44,8 @@ export class TokenService {
*
* Have a go at breaking this without hasValidSig...
* With just expiry I can force entry to /fit-track...
* eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE4MzQ5Njc4OTB9.H_r9p5ppIZPi3FUQoN2XfA-MGfO2a3yurNErUnw6iNo
* TOKEN with maxed out expiry
* eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjk5OTk5OTk5OTk5OX0.RAXPOm19cHn5mG7fSyo1K6wamKUX8XXzKsLKpz_Lb3I
* TokenFilter -> Servlet Exception should throw 401's (not 500) for faulty token...
*/
}
Expand All @@ -71,13 +67,18 @@ export class TokenService {

/** Determine if a token is yet to expire */
private hasValidExpiry(): boolean {
const expiry = new Date(1000 * this.decoded()!.exp);
const decoded: Token = this.decode ?? new Token();
// Note :: ?? -> "Nullish coalescing operator"

const expiry = new Date(1000 * decoded.exp);
const now = new Date();

return expiry.getTime() > now.getTime();
}

/** Decode an authorisation token */
private decode(token: any): Token | null {
/** Get the decoded authorisation token */
private get decode(): Token | null {
const token = this.get();
if (!token) return null

try {
Expand Down
2 changes: 1 addition & 1 deletion src/app/presentation/login/login.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ <h2>Login</h2>
<p>Lost Password? <span>reset</span></p>
</div>
<div class="form-control">
<button type="button" (click)="login()">Enter</button>
<button id="login-btn" type="button" (click)="login()">Enter</button>
<p>Don’t have an account? <span>register</span></p>
</div>
</form>
Expand Down
10 changes: 8 additions & 2 deletions src/app/presentation/login/login.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,14 @@ export class LoginComponent {
constructor(private router: Router, private auth: AuthenticationService) { }

login() {
this.auth.login().then(); // TODO
this.router.navigate(['/home']);
const credentials = {
email: '[email protected]',
password: 'test'
}

this.auth.login(credentials).then(
() => console.log('User has been logged in...') // TODO :: log.TRACE
);
}

}

0 comments on commit 24d8632

Please sign in to comment.