Skip to content

Commit a6c6a38

Browse files
authored
Merge pull request btwld#5 from tnramalho/feature/oauth-authorize
feat: add OAuth authentication
2 parents 0f7b9c3 + 54027c7 commit a6c6a38

27 files changed

Lines changed: 5052 additions & 16937 deletions

coverage/coverage-final.json

Lines changed: 27 additions & 525 deletions
Large diffs are not rendered by default.

coverage/coverage-summary.json

Lines changed: 28 additions & 526 deletions
Large diffs are not rendered by default.

coverage/lcov.info

Lines changed: 1063 additions & 15468 deletions
Large diffs are not rendered by default.

packages/rockets-server/README.md

Lines changed: 240 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
- [authRecovery](#authrecovery)
2929
- [refresh](#refresh)
3030
- [authVerify](#authverify)
31+
- [authRouter](#authrouter)
3132
- [user](#user)
3233
- [password](#password)
3334
- [otp](#otp)
@@ -57,6 +58,8 @@ maintaining flexibility for customization and extension.
5758

5859
- **🔐 Complete Authentication System**: JWT tokens, local authentication,
5960
refresh tokens, and password recovery
61+
- **🔗 OAuth Integration**: Support for Google, GitHub, and Apple OAuth
62+
providers by default, with custom providers support
6063
- **👥 User Management**: Full CRUD operations, profile management, and
6164
password history
6265
- **📱 OTP Support**: One-time password generation and validation for secure
@@ -66,7 +69,8 @@ maintaining flexibility for customization and extension.
6669
- **🔧 Highly Configurable**: Extensive configuration options for all modules
6770
- **🏗️ Modular Architecture**: Use only what you need, extend what you want
6871
- **🛡️ Type Safety**: Full TypeScript support with comprehensive interfaces
69-
- **🧪 Testing Support**: Complete testing utilities and fixtures
72+
- **🧪 Testing Support**: Complete testing utilities and fixtures including
73+
e2e tests
7074
- **🔌 Adapter Pattern**: Support for multiple database adapters
7175

7276
### Installation
@@ -119,11 +123,15 @@ provided by the SDK:
119123
import { Entity, OneToMany } from 'typeorm';
120124
import { UserSqliteEntity } from '@concepta/nestjs-typeorm-ext';
121125
import { UserOtpEntity } from './user-otp.entity';
126+
import { FederatedEntity } from './federated.entity';
122127

123128
@Entity()
124129
export class UserEntity extends UserSqliteEntity {
125130
@OneToMany(() => UserOtpEntity, (userOtp) => userOtp.assignee)
126131
userOtps?: UserOtpEntity[];
132+
133+
@OneToMany(() => FederatedEntity, (federated) => federated.assignee)
134+
federatedAccounts?: FederatedEntity[];
127135
}
128136
```
129137

@@ -141,6 +149,20 @@ export class UserOtpEntity extends OtpSqliteEntity {
141149
}
142150
```
143151

152+
```typescript
153+
// entities/federated.entity.ts
154+
import { Entity, ManyToOne } from 'typeorm';
155+
import { ReferenceIdInterface } from '@concepta/nestjs-common';
156+
import { FederatedSqliteEntity } from '@concepta/nestjs-typeorm-ext';
157+
import { UserEntity } from './user.entity';
158+
159+
@Entity()
160+
export class FederatedEntity extends FederatedSqliteEntity {
161+
@ManyToOne(() => UserEntity, (user) => user.federatedAccounts)
162+
assignee!: ReferenceIdInterface;
163+
}
164+
```
165+
144166
#### Step 2: Set Up Environment Variables (Production Only)
145167

146168
For production, create a `.env` file with JWT secrets:
@@ -167,6 +189,7 @@ import { RocketsServerModule } from '@bitwild/rockets-server';
167189
import { TypeOrmExtModule } from '@concepta/nestjs-typeorm-ext';
168190
import { UserEntity } from './entities/user.entity';
169191
import { UserOtpEntity } from './entities/user-otp.entity';
192+
import { FederatedEntity } from './entities/federated.entity';
170193

171194
@Module({
172195
imports: [
@@ -182,7 +205,7 @@ import { UserOtpEntity } from './entities/user-otp.entity';
182205
synchronize: true, // Auto-create tables (dev only)
183206
autoLoadEntities: true,
184207
logging: false, // Set to true to see SQL queries
185-
entities: [UserEntity, UserOtpEntity],
208+
entities: [UserEntity, UserOtpEntity, FederatedEntity],
186209
}),
187210

188211
// Rockets SDK configuration - minimal setup
@@ -205,6 +228,15 @@ import { UserOtpEntity } from './entities/user-otp.entity';
205228
}),
206229
],
207230
},
231+
232+
// OPTIONAL: Federated entity imports (required for OAuth)
233+
federated: {
234+
imports: [
235+
TypeOrmExtModule.forFeature({
236+
federated: { entity: FederatedEntity },
237+
}),
238+
],
239+
},
208240
inject: [ConfigService],
209241
useFactory: (configService: ConfigService) => ({
210242
// Required services
@@ -293,6 +325,12 @@ With the basic setup complete, your application now provides these endpoints:
293325
- `PATCH /recovery/password` - Reset password with passcode
294326
- `GET /recovery/passcode/:passcode` - Validate recovery passcode
295327

328+
#### OAuth Endpoints
329+
330+
- `GET /oauth/authorize` - Redirect to OAuth provider (Google, GitHub, Apple)
331+
- `GET /oauth/callback` - Handle OAuth callback and return tokens
332+
- `POST /oauth/callback` - Handle OAuth callback via POST method
333+
296334
#### User Management Endpoints
297335

298336
- `GET /user` - Get current user profile
@@ -358,8 +396,8 @@ Expected response (200 OK):
358396
}
359397
```
360398

361-
**Note**: The login endpoint returns a 200 OK status (not 201 Created) as it's retrieving
362-
tokens, not creating a new resource.
399+
**Note**: The login endpoint returns a 200 OK status (not 201 Created) as it's
400+
retrieving tokens, not creating a new resource.
363401

364402
**Defaults Working**: All authentication endpoints work out-of-the-box with
365403
sensible defaults.
@@ -416,8 +454,34 @@ Expected OTP confirm response (200 OK):
416454
}
417455
```
418456

457+
#### 6. Test OAuth Functionality
458+
459+
```bash
460+
# Redirect to Google OAuth (returns 200 OK)
461+
curl -X GET "http://localhost:3000/oauth/authorize?provider=google&scopes=email,profile"
462+
463+
# Redirect to GitHub OAuth (returns 200 OK)
464+
curl -X GET "http://localhost:3000/oauth/authorize?provider=github&scopes=user,email"
465+
466+
# Redirect to Apple OAuth (returns 200 OK)
467+
curl -X GET "http://localhost:3000/oauth/authorize?provider=apple&scopes=email,name"
468+
469+
# Handle OAuth callback (returns 200 OK with tokens)
470+
curl -X GET "http://localhost:3000/oauth/callback?provider=google"
471+
```
472+
473+
Expected OAuth callback response (200 OK):
474+
475+
```json
476+
{
477+
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
478+
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
479+
}
480+
```
481+
419482
🎉 **Congratulations!** You now have a fully functional authentication system
420-
with user management, JWT tokens, and API documentation running with minimal configuration.
483+
with user management, JWT tokens, OAuth integration, and API documentation
484+
running with minimal configuration.
421485

422486
**💡 Pro Tip**: Since we're using an in-memory database, all data is lost when
423487
you restart the application. This is perfect for testing and development!
@@ -482,6 +546,7 @@ interface RocketsServerOptionsInterface {
482546
authRecovery?: AuthRecoveryOptionsInterface;
483547
refresh?: AuthRefreshOptions;
484548
authVerify?: AuthVerifyOptionsInterface;
549+
authRouter?: AuthRouterOptionsInterface;
485550
user?: UserOptionsInterface;
486551
password?: PasswordOptionsInterface;
487552
otp?: OtpOptionsInterface;
@@ -829,6 +894,51 @@ authVerify: {
829894

830895
---
831896

897+
### authRouter
898+
899+
**What it does**: OAuth router configuration that handles routing to different
900+
OAuth providers (Google, GitHub, Apple) based on the provider parameter in
901+
the request.
902+
903+
**Core modules it connects to**: AuthRouterModule, provides OAuth routing and
904+
guards
905+
906+
**When to update**: When you need to add or remove OAuth providers, customize
907+
OAuth guard behavior, or modify OAuth routing logic.
908+
909+
**Real-world example**: Custom OAuth configuration with multiple providers:
910+
911+
```typescript
912+
authRouter: {
913+
guards: [
914+
{ name: 'google', guard: AuthGoogleGuard },
915+
{ name: 'github', guard: AuthGithubGuard },
916+
{ name: 'apple', guard: AuthAppleGuard },
917+
// Add custom OAuth providers
918+
{ name: 'custom', guard: CustomOAuthGuard },
919+
],
920+
settings: {
921+
// Custom OAuth router settings
922+
defaultProvider: 'google',
923+
enableProviderValidation: true,
924+
},
925+
}
926+
```
927+
928+
**Default Configuration**: The SDK automatically configures Google, GitHub, and
929+
Apple OAuth providers with sensible defaults.
930+
931+
**OAuth Flow**:
932+
933+
1. Client calls `/oauth/authorize?provider=google&scopes=email profile`
934+
2. AuthRouterGuard routes to the appropriate OAuth guard based on provider
935+
3. OAuth guard redirects to the provider's authorization URL
936+
4. User authenticates with the OAuth provider
937+
5. Provider redirects back to `/oauth/callback?provider=google`
938+
6. AuthRouterGuard processes the callback and returns JWT tokens
939+
940+
---
941+
832942
### user
833943

834944
**What it does**: User management configuration including CRUD operations,
@@ -1323,7 +1433,99 @@ services: {
13231433

13241434
### Core Concepts
13251435

1326-
#### 1. Authentication Flow
1436+
#### 1. Testing Support
1437+
1438+
The Rockets SDK provides comprehensive testing support including:
1439+
1440+
**Unit Tests**: Individual module and service testing with mock dependencies
1441+
**Integration Tests**: End-to-end testing of complete authentication flows
1442+
**E2E Tests**: Full application testing with real HTTP requests
1443+
1444+
**Example E2E Test Structure**:
1445+
1446+
```typescript
1447+
// auth-oauth.controller.e2e-spec.ts
1448+
describe('AuthOAuthController (e2e)', () => {
1449+
let app: INestApplication;
1450+
1451+
beforeAll(async () => {
1452+
const moduleFixture: TestingModule = await Test.createTestingModule({
1453+
imports: [
1454+
TypeOrmExtModule.forRootAsync({
1455+
useFactory: () => ormConfig,
1456+
}),
1457+
RocketsServerModule.forRoot({
1458+
user: {
1459+
imports: [
1460+
TypeOrmExtModule.forFeature({
1461+
user: { entity: UserFixture },
1462+
}),
1463+
],
1464+
},
1465+
otp: {
1466+
imports: [
1467+
TypeOrmExtModule.forFeature({
1468+
userOtp: { entity: UserOtpEntityFixture },
1469+
}),
1470+
],
1471+
},
1472+
federated: {
1473+
imports: [
1474+
TypeOrmExtModule.forFeature({
1475+
federated: { entity: FederatedEntityFixture },
1476+
}),
1477+
],
1478+
},
1479+
services: {
1480+
mailerService: mockEmailService,
1481+
},
1482+
}),
1483+
],
1484+
controllers: [AuthOAuthController],
1485+
}).compile();
1486+
1487+
app = moduleFixture.createNestApplication();
1488+
app.useGlobalPipes(new ValidationPipe());
1489+
await app.init();
1490+
});
1491+
1492+
afterAll(async () => {
1493+
await app.close();
1494+
});
1495+
1496+
describe('GET /oauth/authorize', () => {
1497+
it('should handle authorize with google provider', async () => {
1498+
await request(app.getHttpServer())
1499+
.get('/oauth/authorize?provider=google&scopes=email profile')
1500+
.expect(200);
1501+
});
1502+
});
1503+
1504+
describe('GET /oauth/callback', () => {
1505+
it('should handle callback with google provider and return tokens', async () => {
1506+
const response = await request(app.getHttpServer())
1507+
.get('/oauth/callback?provider=google')
1508+
.expect(200);
1509+
1510+
expect(mockIssueTokenService.responsePayload).toHaveBeenCalledWith('test-user-id');
1511+
expect(response.body).toEqual({
1512+
accessToken: 'mock-access-token',
1513+
refreshToken: 'mock-refresh-token',
1514+
});
1515+
});
1516+
});
1517+
});
1518+
```
1519+
1520+
**Key Testing Features**:
1521+
1522+
- **Fixture Support**: Pre-built test entities and services
1523+
- **Mock Services**: Easy mocking of email, OTP, and authentication services
1524+
- **Database Testing**: In-memory database support for isolated tests
1525+
- **Guard Testing**: Comprehensive testing of authentication guards
1526+
- **Error Scenarios**: Testing of error conditions and edge cases
1527+
1528+
#### 2. Authentication Flow
13271529

13281530
The Rockets SDK implements a comprehensive authentication flow:
13291531

@@ -1608,6 +1810,38 @@ sequenceDiagram
16081810
- `UserPasswordService` - Custom password hashing, policies
16091811
- `NotificationService` - Custom success notifications
16101812

1813+
#### 5. OAuth Flow
1814+
1815+
The Rockets SDK implements a comprehensive OAuth flow for third-party authentication:
1816+
1817+
#### 5a. OAuth Authorization Flow
1818+
1819+
```mermaid
1820+
sequenceDiagram
1821+
participant C as Client
1822+
participant AR as AuthRouterGuard
1823+
participant AG as AuthGoogleGuard
1824+
participant G as Google OAuth
1825+
participant C as Client
1826+
1827+
C->>AR: GET /oauth/authorize?provider=google&scopes=email profile
1828+
AR->>AR: Route to AuthGoogleGuard
1829+
AR->>AG: canActivate(context)
1830+
AG->>G: Redirect to Google OAuth URL
1831+
G-->>C: Google Login Page
1832+
C->>G: User Authenticates
1833+
G->>C: Redirect to /oauth/callback?code=xyz
1834+
```
1835+
1836+
**Services to customize for OAuth:**
1837+
1838+
- `AuthRouterGuard` - Custom OAuth routing logic, provider validation
1839+
- `AuthGoogleGuard` / `AuthGithubGuard` / `AuthAppleGuard` - Custom OAuth
1840+
provider integration
1841+
- `FederatedModule` - Custom user creation/lookup from OAuth data
1842+
- `UserModelService` - Custom user creation and lookup logic
1843+
- `IssueTokenService` - Custom token generation for OAuth users
1844+
16111845
---
16121846

16131847
This comprehensive documentation provides developers with everything they need

0 commit comments

Comments
 (0)