Add auth-client library for Elixir auth service integration
- Complete Angular client library for authentication and authorization - JWT token management with automatic refresh and storage - OAuth integration with social providers (Google, GitHub, etc.) - Two-factor authentication support (TOTP and backup codes) - Route guards for authentication and scope-based authorization - HTTP interceptor for automatic token injection and refresh - Comprehensive TypeScript interfaces for all API models - User management features (profile updates, password changes) - Cross-tab synchronization and token validation - Complete usage guide with practical examples 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
468
projects/auth-client/README.md
Normal file
468
projects/auth-client/README.md
Normal file
@@ -0,0 +1,468 @@
|
||||
# Auth Client Library
|
||||
|
||||
Angular client library for integrating with the Elixir-based auth service. Provides authentication, authorization, and user management capabilities.
|
||||
|
||||
## Features
|
||||
|
||||
- **Authentication**: Login, register, logout with JWT tokens
|
||||
- **Token Management**: Automatic token refresh and storage
|
||||
- **OAuth Integration**: Social authentication with multiple providers
|
||||
- **Two-Factor Authentication**: TOTP and backup codes support
|
||||
- **Route Guards**: Protect routes based on authentication and scopes
|
||||
- **HTTP Interceptor**: Automatic token injection and refresh
|
||||
- **TypeScript Support**: Fully typed interfaces and models
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install auth-client
|
||||
```
|
||||
|
||||
## Setup
|
||||
|
||||
### 1. Import the HTTP Client Module
|
||||
|
||||
```typescript
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
HttpClientModule,
|
||||
// ... other imports
|
||||
],
|
||||
})
|
||||
export class AppModule {}
|
||||
```
|
||||
|
||||
### 2. Configure the Auth Service
|
||||
|
||||
```typescript
|
||||
import { AuthService } from 'auth-client';
|
||||
|
||||
export class AppComponent {
|
||||
constructor(private authService: AuthService) {
|
||||
// Configure the base URL for your auth service
|
||||
this.authService.configure('http://localhost:4000');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Add HTTP Interceptor (Optional)
|
||||
|
||||
```typescript
|
||||
import { HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||
import { AuthInterceptor } from 'auth-client';
|
||||
|
||||
@NgModule({
|
||||
providers: [
|
||||
{
|
||||
provide: HTTP_INTERCEPTORS,
|
||||
useClass: AuthInterceptor,
|
||||
multi: true
|
||||
}
|
||||
]
|
||||
})
|
||||
export class AppModule {}
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Authentication
|
||||
|
||||
#### Login
|
||||
|
||||
```typescript
|
||||
import { AuthService } from 'auth-client';
|
||||
|
||||
constructor(private authService: AuthService) {}
|
||||
|
||||
login() {
|
||||
this.authService.login({
|
||||
email: 'user@example.com',
|
||||
password: 'password123'
|
||||
}).subscribe({
|
||||
next: (response) => {
|
||||
console.log('Login successful:', response.user);
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Login failed:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
#### Register
|
||||
|
||||
```typescript
|
||||
register() {
|
||||
this.authService.register({
|
||||
email: 'user@example.com',
|
||||
password: 'password123',
|
||||
first_name: 'John',
|
||||
last_name: 'Doe'
|
||||
}).subscribe({
|
||||
next: (response) => {
|
||||
console.log('Registration successful:', response.user);
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Registration failed:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
#### Logout
|
||||
|
||||
```typescript
|
||||
logout() {
|
||||
this.authService.logout().subscribe({
|
||||
next: () => {
|
||||
console.log('Logout successful');
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Logout failed:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Authentication State
|
||||
|
||||
```typescript
|
||||
import { AuthService } from 'auth-client';
|
||||
|
||||
constructor(private authService: AuthService) {
|
||||
// Subscribe to authentication state
|
||||
this.authService.isAuthenticated$.subscribe(isAuth => {
|
||||
console.log('Is authenticated:', isAuth);
|
||||
});
|
||||
|
||||
// Subscribe to current user
|
||||
this.authService.currentUser$.subscribe(user => {
|
||||
console.log('Current user:', user);
|
||||
});
|
||||
|
||||
// Check if authenticated synchronously
|
||||
const isAuth = this.authService.isAuthenticated();
|
||||
}
|
||||
```
|
||||
|
||||
### Route Guards
|
||||
|
||||
#### Protect Authenticated Routes
|
||||
|
||||
```typescript
|
||||
import { AuthGuard } from 'auth-client';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: 'dashboard',
|
||||
component: DashboardComponent,
|
||||
canActivate: [AuthGuard]
|
||||
},
|
||||
{
|
||||
path: 'admin',
|
||||
component: AdminComponent,
|
||||
canActivate: [AuthGuard],
|
||||
data: {
|
||||
requiredScopes: ['admin'], // Require admin scope
|
||||
requireAllScopes: true, // Must have all scopes (default: false)
|
||||
unauthorizedRedirect: '/access-denied'
|
||||
}
|
||||
}
|
||||
];
|
||||
```
|
||||
|
||||
#### Protect Guest Routes
|
||||
|
||||
```typescript
|
||||
import { GuestGuard } from 'auth-client';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: 'login',
|
||||
component: LoginComponent,
|
||||
canActivate: [GuestGuard], // Redirect to home if already authenticated
|
||||
data: {
|
||||
authenticatedRedirect: '/dashboard'
|
||||
}
|
||||
}
|
||||
];
|
||||
```
|
||||
|
||||
### OAuth Integration
|
||||
|
||||
#### Get Available Providers
|
||||
|
||||
```typescript
|
||||
import { OAuthService } from 'auth-client';
|
||||
|
||||
constructor(private oauthService: OAuthService) {}
|
||||
|
||||
getProviders() {
|
||||
this.oauthService.getProviders().subscribe(providers => {
|
||||
console.log('Available providers:', providers);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
#### Initiate OAuth Flow
|
||||
|
||||
```typescript
|
||||
// Redirect flow
|
||||
loginWithGoogle() {
|
||||
this.oauthService.initiateOAuthFlow('google', 'http://localhost:4200/oauth/callback');
|
||||
}
|
||||
|
||||
// Popup flow
|
||||
async loginWithGooglePopup() {
|
||||
try {
|
||||
const result = await this.oauthService.initiateOAuthPopup('google');
|
||||
console.log('OAuth successful:', result);
|
||||
} catch (error) {
|
||||
console.error('OAuth failed:', error);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Handle OAuth Callback
|
||||
|
||||
```typescript
|
||||
// In your callback component
|
||||
ngOnInit() {
|
||||
this.oauthService.completeOAuthFlow('google').subscribe({
|
||||
next: (result) => {
|
||||
console.log('OAuth login successful:', result);
|
||||
this.router.navigate(['/dashboard']);
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('OAuth login failed:', error);
|
||||
this.router.navigate(['/login']);
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Two-Factor Authentication
|
||||
|
||||
#### Setup 2FA
|
||||
|
||||
```typescript
|
||||
setup2FA() {
|
||||
this.authService.setup2FA().subscribe({
|
||||
next: (response) => {
|
||||
// Display QR code and backup codes
|
||||
console.log('QR Code:', response.qr_code);
|
||||
console.log('Backup codes:', response.backup_codes);
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('2FA setup failed:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
#### Verify 2FA Setup
|
||||
|
||||
```typescript
|
||||
verify2FA(token: string) {
|
||||
this.authService.verify2FASetup({ token }).subscribe({
|
||||
next: () => {
|
||||
console.log('2FA enabled successfully');
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('2FA verification failed:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### User Management
|
||||
|
||||
#### Update Profile
|
||||
|
||||
```typescript
|
||||
updateProfile() {
|
||||
this.authService.updateProfile({
|
||||
first_name: 'Jane',
|
||||
last_name: 'Smith'
|
||||
}).subscribe({
|
||||
next: (user) => {
|
||||
console.log('Profile updated:', user);
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Profile update failed:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
#### Change Password
|
||||
|
||||
```typescript
|
||||
changePassword() {
|
||||
this.authService.changePassword({
|
||||
current_password: 'oldpassword',
|
||||
new_password: 'newpassword',
|
||||
new_password_confirmation: 'newpassword'
|
||||
}).subscribe({
|
||||
next: () => {
|
||||
console.log('Password changed successfully');
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Password change failed:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Token Management
|
||||
|
||||
#### Manual Token Operations
|
||||
|
||||
```typescript
|
||||
import { TokenService } from 'auth-client';
|
||||
|
||||
constructor(private tokenService: TokenService) {}
|
||||
|
||||
checkToken() {
|
||||
// Check if token exists and is valid
|
||||
const isValid = this.tokenService.isTokenValid();
|
||||
|
||||
// Get user information from token
|
||||
const userId = this.tokenService.getUserId();
|
||||
const email = this.tokenService.getUserEmail();
|
||||
const scopes = this.tokenService.getUserScopes();
|
||||
|
||||
// Check scopes
|
||||
const hasAdminScope = this.tokenService.hasScope('admin');
|
||||
const hasAnyScope = this.tokenService.hasAnyScope(['read', 'write']);
|
||||
const hasAllScopes = this.tokenService.hasAllScopes(['read', 'write']);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### Models
|
||||
|
||||
All TypeScript interfaces are available for import:
|
||||
|
||||
```typescript
|
||||
import {
|
||||
User,
|
||||
LoginRequest,
|
||||
LoginResponse,
|
||||
RegisterRequest,
|
||||
TokenPair,
|
||||
ApiError,
|
||||
// ... and more
|
||||
} from 'auth-client';
|
||||
```
|
||||
|
||||
### Services
|
||||
|
||||
- **AuthService**: Main authentication service
|
||||
- **TokenService**: Token management and validation
|
||||
- **OAuthService**: OAuth provider integration
|
||||
- **AuthHttpService**: Low-level HTTP client
|
||||
|
||||
### Guards
|
||||
|
||||
- **AuthGuard**: Protect authenticated routes
|
||||
- **GuestGuard**: Protect guest-only routes
|
||||
|
||||
### Interceptors
|
||||
|
||||
- **AuthInterceptor**: Automatic token injection and refresh
|
||||
|
||||
## Configuration
|
||||
|
||||
### Environment Variables
|
||||
|
||||
You can configure the auth service URL through your environment:
|
||||
|
||||
```typescript
|
||||
// environment.ts
|
||||
export const environment = {
|
||||
authServiceUrl: 'http://localhost:4000'
|
||||
};
|
||||
|
||||
// app.component.ts
|
||||
constructor(private authService: AuthService) {
|
||||
this.authService.configure(environment.authServiceUrl);
|
||||
}
|
||||
```
|
||||
|
||||
### Token Storage
|
||||
|
||||
By default, tokens are stored in localStorage. The library handles:
|
||||
|
||||
- Automatic token refresh before expiration
|
||||
- Cross-tab synchronization
|
||||
- Token validation and cleanup
|
||||
|
||||
## Error Handling
|
||||
|
||||
All services return Observable streams with proper error handling:
|
||||
|
||||
```typescript
|
||||
this.authService.login(credentials).subscribe({
|
||||
next: (response) => {
|
||||
// Handle success
|
||||
},
|
||||
error: (apiError: ApiError) => {
|
||||
console.error('Error:', apiError.error);
|
||||
|
||||
// Handle specific errors
|
||||
if (apiError.requires_2fa) {
|
||||
// Redirect to 2FA input
|
||||
}
|
||||
|
||||
if (apiError.details) {
|
||||
// Handle validation errors
|
||||
console.error('Validation errors:', apiError.details);
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
To build the library, run:
|
||||
|
||||
```bash
|
||||
ng build auth-client
|
||||
```
|
||||
|
||||
This command will compile your project, and the build artifacts will be placed in the `dist/` directory.
|
||||
|
||||
## Publishing the Library
|
||||
|
||||
Once the project is built, you can publish your library by following these steps:
|
||||
|
||||
1. Navigate to the `dist` directory:
|
||||
```bash
|
||||
cd dist/auth-client
|
||||
```
|
||||
|
||||
2. Run the `npm publish` command to publish your library to the npm registry:
|
||||
```bash
|
||||
npm publish
|
||||
```
|
||||
|
||||
## Running unit tests
|
||||
|
||||
To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
|
||||
|
||||
```bash
|
||||
ng test auth-client
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
This library is designed to work with the Elixir auth service. Please ensure API compatibility when making changes.
|
||||
|
||||
## License
|
||||
|
||||
MIT License
|
||||
Reference in New Issue
Block a user