@hazeljs/oauth
v1.0.2
Published
OAuth 2.0 and SAML SSO module for HazelJS - Google, Microsoft, GitHub, and enterprise IdPs
Maintainers
Readme
@hazeljs/oauth
OAuth social login + native SAML SSO in one HazelJS package.
Google, Microsoft, GitHub, Facebook, Twitter — plus native SAML IdP support for enterprise SSO. One config, ready-made routes, PKCE, user profiles, callback handler hooks, JWT integration.
Features
- Multi-Provider OAuth — Google, Microsoft Entra ID, GitHub, Facebook, Twitter
- Native SAML SP — Multi-IdP configuration with ACS callback and metadata endpoint
- PKCE Support — Automatic for Google, Microsoft, Twitter
- User Profile — Fetches id, email, name, picture from provider APIs
- Ready-Made Routes — Optional
/auth/:providerand/auth/:provider/callback - JWT Integration — Use with
@hazeljs/authfor session tokens
Installation
npm install @hazeljs/oauthOr with the HazelJS CLI:
hazel add oauthQuick Start
1. Configure Providers
import { HazelModule } from '@hazeljs/core';
import { OAuthModule } from '@hazeljs/oauth';
@HazelModule({
imports: [
OAuthModule.forRoot({
providers: {
google: {
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
redirectUri: process.env.OAUTH_REDIRECT_URI!,
},
github: {
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
redirectUri: process.env.OAUTH_REDIRECT_URI!,
},
},
}),
],
})
export class AppModule {}2. Use Built-in Routes
The OAuthController provides:
- GET /auth/:provider — Redirects to provider (google, microsoft, github, facebook, twitter)
- GET /auth/:provider/callback — Handles callback, returns
{ accessToken, user } - GET /auth/saml/:idp — Redirects to SAML IdP with AuthnRequest
- POST /auth/saml/:idp/callback — Handles SAML ACS callback
- GET /auth/saml/:idp/metadata — Returns SP metadata XML
Example: User visits /auth/google → authenticates → callback returns tokens and profile.
3. Custom Flow with OAuthService
import { OAuthService } from '@hazeljs/oauth';
@Injectable()
export class AuthController {
constructor(private oauth: OAuthService) {}
@Get('login/:provider')
login(@Param('provider') provider: string, @Res() res: Response) {
const { url, state, codeVerifier } = this.oauth.getAuthorizationUrl(provider);
// Store state + codeVerifier in cookies
res.redirect(url);
}
@Get('callback')
async callback(@Query() q: { code: string; state: string }, @Req() req: Request) {
const { codeVerifier } = getFromCookies(req);
const result = await this.oauth.handleCallback('google', q.code, q.state, codeVerifier);
// result: { accessToken, refreshToken?, expiresAt?, user }
return result;
}
}Supported Providers
| Provider | PKCE | Default Scopes | | --------- | ---- | ---------------------- | | Google | Yes | openid, profile, email | | Microsoft | Yes | openid, profile, email | | GitHub | No | user:email | | Facebook | No | email, public_profile | | Twitter | Yes | users.read, tweet.read |
SAML Configuration (Multi-IdP)
OAuthModule.forRoot({
providers: {
google: { ...googleConfig },
saml: {
oktaMain: {
idpKey: 'okta-main',
ssoUrl: 'https://your-org.okta.com/app/abc/sso/saml',
issuer: 'https://api.example.com',
acsUrl: 'https://api.example.com/auth/saml/okta-main/callback',
audience: 'https://api.example.com',
relayState: 'app=dashboard',
},
azureMain: {
idpKey: 'azure-main',
ssoUrl: 'https://login.microsoftonline.com/.../saml2',
issuer: 'https://api.example.com',
acsUrl: 'https://api.example.com/auth/saml/azure-main/callback',
},
},
},
});Use start + callback flow:
- Browser hits
GET /auth/saml/okta-main - IdP authenticates user and POSTS
SAMLResponseto ACS callback - Package parses assertion, extracts profile, then invokes optional callback handler
Configuration
Microsoft (optional tenant)
microsoft: {
clientId: '...',
clientSecret: '...',
redirectUri: '...',
tenant: 'common', // or your Azure AD tenant ID
}Twitter (optional client secret)
twitter: {
clientId: '...',
redirectUri: '...',
clientSecret: null, // for public clients (PKCE-only)
}Custom Scopes
OAuthModule.forRoot({
providers: { google: {...} },
defaultScopes: {
google: ['openid', 'profile', 'email', 'https://www.googleapis.com/auth/calendar'],
},
});Integration with @hazeljs/auth
After OAuth callback, create a user and issue JWT:
import { JwtService } from '@hazeljs/auth';
import { OAuthService } from '@hazeljs/oauth';
async handleOAuthCallback(provider: string, code: string, state: string, codeVerifier?: string) {
const { user, accessToken } = await this.oauth.handleCallback(provider, code, state, codeVerifier);
const dbUser = await this.upsertUser(user);
const jwt = this.jwt.sign({ sub: dbUser.id, email: dbUser.email });
return { user: dbUser, accessToken: jwt };
}Environment Variables
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
MICROSOFT_CLIENT_ID=
MICROSOFT_CLIENT_SECRET=
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
FACEBOOK_CLIENT_ID=
FACEBOOK_CLIENT_SECRET=
TWITTER_CLIENT_ID=
TWITTER_CLIENT_SECRET=
OAUTH_REDIRECT_URI=http://localhost:3000/auth/google/callbackAPI Reference
OAuthService
getAuthorizationUrl(provider, state?, scopes?)— Returns{ url, state, codeVerifier? }handleCallback(provider, code, state, codeVerifier?)— Returns{ accessToken, refreshToken?, expiresAt?, user }validateState(received, stored)— CSRF checkgenerateState()— Cryptographically secure stategetSamlAuthorizationUrl(idpKey, relayState?)— Returns SAML redirect URL + request IDhandleSamlCallback(idpKey, samlResponseBase64, relayState?)— Returns parsed SAML callback resultgetSamlMetadata(idpKey)— Returns SP metadata XMLgetSamlProviderKeys()— Returns configured SAML provider keys
OAuthStateGuard
Validates the state parameter on callback. Expects oauth_stored_state on the request.
Testing
npm testLinks
License
Apache 2.0 © HazelJS
