Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Feature/login locked #342

Open
wants to merge 24 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
f850adf
chore: add auth history feature
tnramalho Jan 16, 2025
fbe1ed8
chore: linting
tnramalho Jan 16, 2025
2b0eec3
Merge branch 'main' into feature/login-history
tnramalho Jan 18, 2025
972fbe4
chore: packages updates
tnramalho Jan 18, 2025
e982f8c
chore: add events and tests
tnramalho Jan 22, 2025
a14a01e
feat: add locked user logic
tnramalho Jan 25, 2025
34e3691
chore: yarn update
tnramalho Jan 25, 2025
c5fd9e3
Merge branch 'feature/login-history' into feature/login-locked
tnramalho Jan 25, 2025
0a0ea2a
Merge branch 'main' into feature/login-history
tnramalho Jan 26, 2025
dc4592e
Merge branch 'feature/login-history' into feature/login-locked
tnramalho Jan 26, 2025
fe3345f
Merge branch 'main' into feature/login-history
tnramalho Jan 28, 2025
d7c8f85
chore: update file name
tnramalho Jan 28, 2025
8d81736
Merge branch 'feature/login-history' into feature/login-locked
tnramalho Jan 28, 2025
de1511e
Merge branch 'main' into feature/login-history
tnramalho Jan 29, 2025
f60e8fd
chore: update events
tnramalho Jan 29, 2025
2b4ea1d
Merge branch 'feature/login-history' into feature/login-locked
tnramalho Jan 29, 2025
8ffbc61
chore: linting
tnramalho Jan 29, 2025
8bf1377
chore: move dispatch events to strategy
tnramalho Feb 4, 2025
1d0870d
Merge branch 'main' into feature/login-locked
tnramalho Feb 6, 2025
249440a
chore: make properties optional
tnramalho Feb 6, 2025
feaca0e
chore: remove decorator
tnramalho Feb 12, 2025
860fb67
Merge branch 'main' into feature/login-locked
tnramalho Mar 3, 2025
e7bc8e6
chore: update action/cache version
tnramalho Mar 3, 2025
3090ca2
Merge branch 'main' into feature/login-locked
tnramalho Mar 4, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/nestjs-auth-apple/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"dependencies": {
"@concepta/nestjs-authentication": "^6.0.0-alpha.2",
"@concepta/nestjs-common": "^6.0.0-alpha.2",
"@concepta/nestjs-event": "^6.0.0-alpha.2",
"@concepta/nestjs-exception": "^6.0.0-alpha.2",
"@concepta/nestjs-federated": "^6.0.0-alpha.2",
"@concepta/nestjs-jwt": "^6.0.0-alpha.2",
Expand Down
2 changes: 2 additions & 0 deletions packages/nestjs-auth-apple/src/auth-apple.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ export const AUTH_APPLE_VERIFY_ALGORITHM = 'RS256';
export const AUTH_APPLE_TOKEN_ISSUER = 'https://appleid.apple.com';

export const AUTH_APPLE_JWT_KEYS = 'https://appleid.apple.com/auth/keys';

export const AUTH_APPLE_AUTHENTICATION_TYPE = 'auth-apple';
2 changes: 2 additions & 0 deletions packages/nestjs-auth-apple/src/auth-apple.module.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { AuthAppleModule } from './auth-apple.module';

import { FederatedEntityFixture } from './__fixtures__/federated-entity.fixture';
import { UserEntityFixture } from './__fixtures__/user.entity.fixture';
import { EventModule } from '@concepta/nestjs-event';

describe(AuthAppleModule, () => {
let authAppleModule: AuthAppleModule;
Expand All @@ -31,6 +32,7 @@ describe(AuthAppleModule, () => {
entities: [UserEntityFixture, FederatedEntityFixture],
}),
JwtModule.forRoot({}),
EventModule.forRoot({}),
AuthAppleModule.forRoot({}),
AuthenticationModule.forRoot({}),
AuthJwtModule.forRootAsync({
Expand Down
33 changes: 32 additions & 1 deletion packages/nestjs-auth-apple/src/auth-apple.strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from '@concepta/nestjs-federated';

import {
AUTH_APPLE_AUTHENTICATION_TYPE,
AUTH_APPLE_MODULE_SETTINGS_TOKEN,
AUTH_APPLE_SERVICE_TOKEN,
AUTH_APPLE_STRATEGY_NAME,
Expand All @@ -19,6 +20,11 @@ import { Strategy } from 'passport-apple';
import { AuthAppleServiceInterface } from './interfaces/auth-apple-service.interface';
import { mapProfile } from './utils/auth-apple-map-profile';
import { AuthAppleCredentialsInterface } from './interfaces/auth-apple-credentials.interface';
import { AuthAppleAuthenticatedEventAsync } from './events/auth-apple-authenticated.event';
import {
AuthenticationRequestInterface,
getAuthenticatedUserInfo,
} from '@concepta/nestjs-authentication';

@Injectable()
export class AuthAppleStrategy extends PassportStrategy(
Expand All @@ -41,10 +47,11 @@ export class AuthAppleStrategy extends PassportStrategy(
privateKeyString: settings?.privateKeyString,
callbackURL: settings?.callbackURL,
scope: settings?.scope,
passReqToCallback: false,
passReqToCallback: true,
});
}
async validate(
req: AuthenticationRequestInterface,
_accessToken: string,
_refreshToken: string,
idToken: string,
Expand Down Expand Up @@ -73,6 +80,7 @@ export class AuthAppleStrategy extends PassportStrategy(
throw new UnauthorizedException();
}

await this.dispatchAuthAttemptEvent(req, user.id, true);
return user;
}

Expand All @@ -87,4 +95,27 @@ export class AuthAppleStrategy extends PassportStrategy(
throw new AuthAppleMissingEmailException();
}
}

protected async dispatchAuthAttemptEvent(
req: AuthenticationRequestInterface,
userId: string,
success: boolean,
failureReason?: string | null,
): Promise<void> {
const info = getAuthenticatedUserInfo(req);

const failMessage = failureReason ? { failureReason } : {};
const authenticatedEventAsync = new AuthAppleAuthenticatedEventAsync({
userInfo: {
userId: userId,
ipAddress: info.ipAddress || '',
deviceInfo: info.deviceInfo || '',
authType: AUTH_APPLE_AUTHENTICATION_TYPE,
success,
...failMessage,
},
});

await authenticatedEventAsync.emit();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { AuthenticatedEventInterface } from '@concepta/nestjs-common';
import { EventAsync } from '@concepta/nestjs-event';

export class AuthAppleAuthenticatedEventAsync extends EventAsync<
AuthenticatedEventInterface,
boolean
> {}
1 change: 1 addition & 0 deletions packages/nestjs-auth-github/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"dependencies": {
"@concepta/nestjs-authentication": "^6.0.0-alpha.2",
"@concepta/nestjs-common": "^6.0.0-alpha.2",
"@concepta/nestjs-event": "^6.0.0-alpha.2",
"@concepta/nestjs-exception": "^6.0.0-alpha.2",
"@concepta/nestjs-federated": "^6.0.0-alpha.2",
"@nestjs/common": "^10.4.1",
Expand Down
2 changes: 2 additions & 0 deletions packages/nestjs-auth-github/src/auth-github.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ export const AUTH_GITHUB_MODULE_DEFAULT_SETTINGS_TOKEN =
'AUTH_GITHUB_MODULE_DEFAULT_SETTINGS_TOKEN';

export const AUTH_GITHUB_STRATEGY_NAME = 'github';

export const AUTH_GITHUB_AUTHENTICATION_TYPE = 'auth-github';
16 changes: 8 additions & 8 deletions packages/nestjs-auth-github/src/auth-github.controller.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { Controller, Inject, Get, UseGuards } from '@nestjs/common';
import { ApiOkResponse, ApiTags } from '@nestjs/swagger';
import {
AuthenticatedUserInterface,
AuthenticationResponseInterface,
} from '@concepta/nestjs-common';
import {
AuthUser,
IssueTokenServiceInterface,
AuthenticationJwtResponseDto,
AuthPublic,
AuthUser,
IssueTokenServiceInterface,
} from '@concepta/nestjs-authentication';
import {
AuthenticatedUserInterface,
AuthenticationResponseInterface,
} from '@concepta/nestjs-common';
import { Controller, Get, Inject, UseGuards } from '@nestjs/common';
import { ApiOkResponse, ApiTags } from '@nestjs/swagger';
import { AUTH_GITHUB_ISSUE_TOKEN_SERVICE_TOKEN } from './auth-github.constants';
import { AuthGithubGuard } from './auth-github.guard';

Expand Down
31 changes: 30 additions & 1 deletion packages/nestjs-auth-github/src/auth-github.strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
} from '@concepta/nestjs-federated';

import {
AUTH_GITHUB_AUTHENTICATION_TYPE,
AUTH_GITHUB_MODULE_SETTINGS_TOKEN,
AUTH_GITHUB_STRATEGY_NAME,
} from './auth-github.constants';
Expand All @@ -17,6 +18,9 @@ import { AuthGithubProfileInterface } from './interfaces/auth-github-profile.int
import { AuthGithubMissingEmailException } from './exceptions/auth-github-missing-email.exception';
import { AuthGithubMissingIdException } from './exceptions/auth-github-missing-id.exception';
import { mapProfile } from './utils/auth-github-map-profile';
import { AuthGithubAuthenticatedEventAsync } from './events/auth-github-authenticated.event';
import { getAuthenticatedUserInfo } from '@concepta/nestjs-authentication/src';
import { AuthenticationRequestInterface } from '@concepta/nestjs-authentication';

@Injectable()
export class AuthGithubStrategy extends PassportStrategy(
Expand All @@ -32,10 +36,12 @@ export class AuthGithubStrategy extends PassportStrategy(
clientID: settings?.clientId,
clientSecret: settings?.clientSecret,
callbackURL: settings?.callbackURL,
passReqToCallback: true,
});
}

async validate(
req: AuthenticationRequestInterface,
_accessToken: string,
_refreshToken: string,
profile: AuthGithubProfileInterface,
Expand All @@ -62,7 +68,30 @@ export class AuthGithubStrategy extends PassportStrategy(
if (!user) {
throw new UnauthorizedException();
}

await this.dispatchAuthAttemptEvent(req, user.id, true);
return user;
}

protected async dispatchAuthAttemptEvent(
req: AuthenticationRequestInterface,
userId: string,
success: boolean,
failureReason?: string | null,
): Promise<void> {
const info = getAuthenticatedUserInfo(req);

const failMessage = failureReason ? { failureReason } : {};
const authenticatedEventAsync = new AuthGithubAuthenticatedEventAsync({
userInfo: {
userId: userId,
ipAddress: info.ipAddress || '',
deviceInfo: info.deviceInfo || '',
authType: AUTH_GITHUB_AUTHENTICATION_TYPE,
success,
...failMessage,
},
});

await authenticatedEventAsync.emit();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { AuthenticatedEventInterface } from '@concepta/nestjs-common';
import { EventAsync } from '@concepta/nestjs-event';

export class AuthGithubAuthenticatedEventAsync extends EventAsync<
AuthenticatedEventInterface,
boolean
> {}
1 change: 1 addition & 0 deletions packages/nestjs-auth-google/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"dependencies": {
"@concepta/nestjs-authentication": "^6.0.0-alpha.2",
"@concepta/nestjs-common": "^6.0.0-alpha.2",
"@concepta/nestjs-event": "^6.0.0-alpha.2",
"@concepta/nestjs-exception": "^6.0.0-alpha.2",
"@concepta/nestjs-federated": "^6.0.0-alpha.2",
"@nestjs/common": "^10.4.1",
Expand Down
2 changes: 2 additions & 0 deletions packages/nestjs-auth-google/src/auth-google.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ export const AUTH_GOOGLE_MODULE_DEFAULT_SETTINGS_TOKEN =
'AUTH_GOOGLE_MODULE_DEFAULT_SETTINGS_TOKEN';

export const AUTH_GOOGLE_STRATEGY_NAME = 'google';

export const AUTH_GOOGLE_AUTHENTICATION_TYPE = 'auth-google';
32 changes: 32 additions & 0 deletions packages/nestjs-auth-google/src/auth-google.strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from '@concepta/nestjs-federated';

import {
AUTH_GOOGLE_AUTHENTICATION_TYPE,
AUTH_GOOGLE_MODULE_SETTINGS_TOKEN,
AUTH_GOOGLE_STRATEGY_NAME,
} from './auth-google.constants';
Expand All @@ -17,6 +18,11 @@ import { AuthGoogleMissingEmailException } from './exceptions/auth-google-missin
import { AuthGoogleMissingIdException } from './exceptions/auth-google-missing-id.exception';
import { mapProfile } from './utils/auth-google-map-profile';
import { Strategy } from 'passport-google-oauth20';
import {
AuthenticationRequestInterface,
getAuthenticatedUserInfo,
} from '@concepta/nestjs-authentication';
import { AuthGoogleAuthenticatedEventAsync } from './events/auth-google-authenticated.event';

@Injectable()
export class AuthGoogleStrategy extends PassportStrategy(
Expand All @@ -33,10 +39,12 @@ export class AuthGoogleStrategy extends PassportStrategy(
clientSecret: settings?.clientSecret,
callbackURL: settings?.callbackURL,
scope: settings?.scope,
passReqToCallback: true,
});
}

async validate(
req: AuthenticationRequestInterface,
_accessToken: string,
_refreshToken: string,
profile: AuthGoogleProfileInterface,
Expand Down Expand Up @@ -64,6 +72,30 @@ export class AuthGoogleStrategy extends PassportStrategy(
throw new UnauthorizedException();
}

await this.dispatchAuthAttemptEvent(req, user.id, true);
return user;
}

protected async dispatchAuthAttemptEvent(
req: AuthenticationRequestInterface,
userId: string,
success: boolean,
failureReason?: string | null,
): Promise<void> {
const info = getAuthenticatedUserInfo(req);

const failMessage = failureReason ? { failureReason } : {};
const authenticatedEventAsync = new AuthGoogleAuthenticatedEventAsync({
userInfo: {
userId: userId,
ipAddress: info.ipAddress || '',
deviceInfo: info.deviceInfo || '',
authType: AUTH_GOOGLE_AUTHENTICATION_TYPE,
success,
...failMessage,
},
});

await authenticatedEventAsync.emit();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { AuthenticatedEventInterface } from '@concepta/nestjs-common';
import { EventAsync } from '@concepta/nestjs-event';

export class AuthGoogleAuthenticatedEventAsync extends EventAsync<
AuthenticatedEventInterface,
boolean
> {}
18 changes: 18 additions & 0 deletions packages/nestjs-auth-history/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Rockets NestJS Auth History

A module for tracking authentication history and events, providing services
for creating, reading, updating and deleting auth history records. Includes
event handling for authenticated requests, repository management, and access
control.

## Project

[![NPM Latest](https://img.shields.io/npm/v/@concepta/nestjs-auth-history)](https://www.npmjs.com/package/@concepta/nestjs-auth-history)
[![NPM Downloads](https://img.shields.io/npm/dw/@conceptadev/nestjs-auth-history)](https://www.npmjs.com/package/@concepta/nestjs-auth-history)
[![GH Last Commit](https://img.shields.io/github/last-commit/conceptadev/rockets?logo=github)](https://github.com/conceptadev/rockets)
[![GH Contrib](https://img.shields.io/github/contributors/conceptadev/rockets?logo=github)](https://github.com/conceptadev/rockets/graphs/contributors)
[![NestJS Dep](https://img.shields.io/github/package-json/dependency-version/conceptadev/rockets/@nestjs/common?label=NestJS&logo=nestjs&filename=packages%2Fnestjs-core%2Fpackage.json)](https://www.npmjs.com/package/@nestjs/common)

## Installation

`yarn add @concepta/nestjs-auth-history`
46 changes: 46 additions & 0 deletions packages/nestjs-auth-history/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"name": "@concepta/nestjs-auth-history",
"version": "6.0.0-alpha.1",
"description": "Rockets NestJS auth history",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"license": "BSD-3-Clause",
"publishConfig": {
"access": "public"
},
"files": [
"dist/**/!(*.spec|*.e2e-spec|*.fixture).{js,d.ts}"
],
"dependencies": {
"@concepta/nestjs-access-control": "^6.0.0-alpha.1",
"@concepta/nestjs-common": "^6.0.0-alpha.1",
"@concepta/nestjs-crud": "^6.0.0-alpha.1",
"@concepta/nestjs-event": "^6.0.0-alpha.1",
"@concepta/nestjs-exception": "^6.0.0-alpha.1",
"@concepta/nestjs-password": "^6.0.0-alpha.1",
"@concepta/nestjs-typeorm-ext": "^6.0.0-alpha.1",
"@concepta/typeorm-common": "^6.0.0-alpha.1",
"@nestjs/common": "^10.4.1",
"@nestjs/config": "^3.2.3",
"@nestjs/core": "^10.4.1",
"@nestjs/swagger": "^7.4.0"
},
"devDependencies": {
"@concepta/nestjs-auth-jwt": "^6.0.0-alpha.1",
"@concepta/nestjs-authentication": "^6.0.0-alpha.1",
"@concepta/nestjs-jwt": "^6.0.0-alpha.1",
"@concepta/nestjs-user": "^6.0.0-alpha.1",
"@concepta/typeorm-seeding": "^4.0.0",
"@faker-js/faker": "^8.4.1",
"@nestjs/testing": "^10.4.1",
"@nestjs/typeorm": "^10.0.2",
"accesscontrol": "^2.2.1",
"supertest": "^6.3.4"
},
"peerDependencies": {
"@concepta/nestjs-auth-local": "^6.0.0-alpha.1",
"class-transformer": "*",
"class-validator": "*",
"typeorm": "^0.3.0"
}
}
Loading