@elchinabilov/nestjs-signin
v1.0.1
Published
A NestJS module for social sign-in authentication with multiple providers (Google, Apple). Verify ID tokens, extract user profiles, and integrate social login into your NestJS application with ease.
Maintainers
Readme
@elchinabilov/nestjs-signin
A powerful and extensible NestJS module for social sign-in authentication. Verify ID tokens from multiple providers (Google, Apple) and get normalized user profiles with a single, unified API.
Features
- Google Sign-In - Verify Google ID tokens using the official
google-auth-library - Apple Sign-In - Verify Apple ID tokens with JWKS public key validation
- Unified API - Single
verifyToken()method for all providers - Normalized User Profile - Consistent
SignInUserinterface across all providers - Dynamic Module - Supports both
forRoot()andforRootAsync()patterns - Global Module - Register once, use anywhere in your application
- Type-Safe - Full TypeScript support with comprehensive type definitions
- Custom Exceptions - Descriptive error types for different failure scenarios
- Built-in DTO - Ready-to-use
VerifyTokenDtowithclass-validatordecorators - Extensible - Easy to add new providers in the future
Installation
npm install @elchinabilov/nestjs-signinPeer Dependencies
Make sure you have the following packages installed in your NestJS project:
npm install @nestjs/common @nestjs/core reflect-metadata rxjsOptional (for DTO validation):
npm install class-validator class-transformerQuick Start
1. Register the Module
import { Module } from '@nestjs/common';
import { SignInModule } from '@elchinabilov/nestjs-signin';
@Module({
imports: [
SignInModule.forRoot({
google: {
clientIds: [
'YOUR_GOOGLE_WEB_CLIENT_ID',
'YOUR_GOOGLE_IOS_CLIENT_ID',
'YOUR_GOOGLE_ANDROID_CLIENT_ID',
],
},
apple: {
clientIds: ['YOUR_APPLE_SERVICE_ID'],
},
}),
],
})
export class AppModule {}2. Use the Service
import { Controller, Post, Body } from '@nestjs/common';
import {
SignInService,
SignInProvider,
SignInUser,
VerifyTokenDto,
} from '@elchinabilov/nestjs-signin';
@Controller('auth')
export class AuthController {
constructor(private readonly signInService: SignInService) {}
@Post('social-login')
async socialLogin(@Body() dto: VerifyTokenDto): Promise<SignInUser> {
const user = await this.signInService.verifyToken(dto.provider, dto.token);
// user.provider -> 'google' | 'apple'
// user.providerId -> unique ID from the provider
// user.email -> user's email (if available)
// user.emailVerified -> whether the email is verified
// user.firstName -> first name (Google only)
// user.lastName -> last name (Google only)
// user.fullName -> full name (Google only)
// user.avatar -> profile picture URL (Google only)
// user.raw -> original payload from the provider
return user;
}
}Async Configuration
Use forRootAsync() when your configuration depends on other services (e.g., ConfigService):
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { SignInModule } from '@elchinabilov/nestjs-signin';
@Module({
imports: [
ConfigModule.forRoot(),
SignInModule.forRootAsync({
imports: [ConfigModule],
useFactory: (configService: ConfigService) => ({
google: {
clientIds: configService
.get<string>('GOOGLE_CLIENT_IDS')!
.split(','),
},
apple: {
clientIds: [configService.get<string>('APPLE_CLIENT_ID')!],
},
}),
inject: [ConfigService],
}),
],
})
export class AppModule {}Using a Factory Class
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import {
SignInModuleOptionsFactory,
SignInModuleConfig,
} from '@elchinabilov/nestjs-signin';
@Injectable()
export class SignInConfigService implements SignInModuleOptionsFactory {
constructor(private readonly configService: ConfigService) {}
createSignInOptions(): SignInModuleConfig {
return {
google: {
clientIds: this.configService
.get<string>('GOOGLE_CLIENT_IDS')!
.split(','),
},
apple: {
clientIds: [this.configService.get<string>('APPLE_CLIENT_ID')!],
},
};
}
}SignInModule.forRootAsync({
imports: [ConfigModule],
useClass: SignInConfigService,
});Single Provider Setup
You can configure only the providers you need. Unconfigured providers will throw a ProviderNotConfiguredException if accessed:
// Google only
SignInModule.forRoot({
google: {
clientIds: ['YOUR_GOOGLE_CLIENT_ID'],
},
});
// Apple only
SignInModule.forRoot({
apple: {
clientIds: ['YOUR_APPLE_SERVICE_ID'],
},
});API Reference
SignInService
| Method | Return Type | Description |
| -------------------------------- | --------------------- | ------------------------------------------------ |
| verifyToken(provider, token) | Promise<SignInUser> | Verifies a token and returns the normalized user |
| getConfiguredProviders() | SignInProvider[] | Returns a list of configured providers |
| isProviderConfigured(provider) | boolean | Checks if a specific provider is configured |
SignInUser Interface
| Field | Type | Description |
| --------------- | ------------------------- | --------------------------------------- |
| provider | SignInProvider | The provider used (google or apple) |
| providerId | string | Unique user ID from the provider |
| email | string | null | User's email address |
| emailVerified | boolean | Whether the email is verified |
| firstName | string | null | User's first name |
| lastName | string | null | User's last name |
| fullName | string | null | User's full name |
| avatar | string | null | Profile picture URL |
| raw | Record<string, unknown> | Original payload from the provider |
SignInProvider Enum
| Value | Description |
| -------- | -------------- |
| google | Google Sign-In |
| apple | Apple Sign-In |
Exceptions
| Exception | HTTP Status | Description |
| -------------------------------- | ----------- | ------------------------------------------ |
| SignInException | 401 | Base exception for all sign-in errors |
| InvalidTokenException | 401 | The provided token is invalid |
| TokenExpiredException | 401 | The provided token has expired |
| ProviderNotConfiguredException | 500 | The requested provider is not configured |
| ProviderVerificationException | 401 | Token verification failed on provider side |
VerifyTokenDto
Pre-built DTO with class-validator decorators:
{
"provider": "google", // 'google' | 'apple'
"token": "eyJhbGciOi..."
}Provider Details
Google Sign-In
Google provider uses the official google-auth-library to verify ID tokens. It supports multiple client IDs (web, iOS, Android).
What you get:
providerId- Google user ID (subclaim)email- User's emailemailVerified- Email verification statusfirstName- Given namelastName- Family namefullName- Display nameavatar- Profile picture URL
Apple Sign-In
Apple provider verifies ID tokens by fetching Apple's public JWKS keys, with built-in caching (24h TTL). Apple provides limited user data.
What you get:
providerId- Apple user ID (subclaim)email- User's email (may be a private relay email)emailVerified- Email verification statusfirstName-null(Apple does not include this in the token)lastName-null(Apple does not include this in the token)fullName-null(Apple does not include this in the token)avatar-null(Apple does not provide this)
Note: Apple only sends user's name on the first authorization. You must capture it from the client-side authorization response and save it in your database.
Error Handling
import {
SignInService,
SignInProvider,
InvalidTokenException,
TokenExpiredException,
ProviderNotConfiguredException,
} from '@elchinabilov/nestjs-signin';
@Controller('auth')
export class AuthController {
constructor(private readonly signInService: SignInService) {}
@Post('social-login')
async socialLogin(@Body() dto: VerifyTokenDto) {
try {
const user = await this.signInService.verifyToken(
dto.provider,
dto.token,
);
return { success: true, user };
} catch (error) {
if (error instanceof InvalidTokenException) {
// Token is invalid or tampered
}
if (error instanceof TokenExpiredException) {
// Token has expired - client should refresh
}
if (error instanceof ProviderNotConfiguredException) {
// Provider not set up in module config
}
throw error;
}
}
}Environment Variables Example
# Google
GOOGLE_CLIENT_IDS=web-client-id.apps.googleusercontent.com,ios-client-id.apps.googleusercontent.com,android-client-id.apps.googleusercontent.com
# Apple
APPLE_CLIENT_ID=com.your.app.serviceRequirements
- Node.js >= 18.0.0
- NestJS >= 9.0.0
License
ISC
