Skip to content

Commit

Permalink
Merge pull request #6 from mjangir/twitch-authentication
Browse files Browse the repository at this point in the history
feat(twitch-auth): add twitch authentication package in nestjs-hybrid…
  • Loading branch information
mjangir authored Oct 3, 2021
2 parents 563bb22 + 00d021f commit 3400250
Show file tree
Hide file tree
Showing 19 changed files with 833 additions and 104 deletions.
5 changes: 5 additions & 0 deletions .changeset/clean-frogs-grin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@nestjs-hybrid-auth/all': minor
---

Added Twitch Authentication in all
5 changes: 5 additions & 0 deletions .changeset/wet-weeks-do.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@nestjs-hybrid-auth/twitch': major
---

First major release of @nestjs-hybrid-auth/twitch
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ class AuthController {
- [LinkedIn](https://nestjs-hybrid-auth.manishjangir.com/docs/providers/linkedin)
- [Twitter](https://nestjs-hybrid-auth.manishjangir.com/docs/providers/twitter)
- [GitHub](https://nestjs-hybrid-auth.manishjangir.com/docs/providers/github)
- [Instagram](https://nestjs-hybrid-auth.manishjangir.com/docs/providers/instagram)
- [Twitch](https://nestjs-hybrid-auth.manishjangir.com/docs/providers/twitch)

## Related

Expand Down
3 changes: 2 additions & 1 deletion packages/nestjs-hybrid-auth-all/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"@nestjs-hybrid-auth/google": "^1.0.0",
"@nestjs-hybrid-auth/linkedin": "^1.0.0",
"@nestjs-hybrid-auth/github": "^1.0.0",
"@nestjs-hybrid-auth/twitter": "^1.0.0"
"@nestjs-hybrid-auth/twitter": "^1.0.0",
"@nestjs-hybrid-auth/twitch": "^0.0.0"
}
}
6 changes: 6 additions & 0 deletions packages/nestjs-hybrid-auth-all/src/hybrid-auth.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { LinkedinAuthModule } from '@nestjs-hybrid-auth/linkedin';
import { FacebookAuthModule } from '@nestjs-hybrid-auth/facebook';
import { InstagramAuthModule } from '@nestjs-hybrid-auth/instagram';
import { GithubAuthModule } from '@nestjs-hybrid-auth/github';
import { TwitchAuthModule } from '@nestjs-hybrid-auth/twitch';
import {
HybridAuthModuleOptions,
HybridAuthModuleAsyncOptions,
Expand All @@ -18,6 +19,7 @@ function createHybridAuthImports(options: HybridAuthModuleOptions): any {
options.facebook && FacebookAuthModule.forRoot(options.facebook),
options.instagram && InstagramAuthModule.forRoot(options.instagram),
options.github && GithubAuthModule.forRoot(options.github),
options.twitch && TwitchAuthModule.forRoot(options.twitch),
].filter(Boolean);
}

Expand Down Expand Up @@ -58,6 +60,10 @@ export class HybridAuthModule {
imports.push(InstagramAuthModule.forRootAsync(options.instagram));
}

if (options.twitch) {
imports.push(TwitchAuthModule.forRootAsync(options.twitch));
}

return {
module: HybridAuthModule,
imports,
Expand Down
6 changes: 6 additions & 0 deletions packages/nestjs-hybrid-auth-all/src/hybrid-auth.options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { LinkedinAuthModuleOptions } from '@nestjs-hybrid-auth/linkedin';
import { FacebookAuthModuleOptions } from '@nestjs-hybrid-auth/facebook';
import { InstagramAuthModuleOptions } from '@nestjs-hybrid-auth/instagram';
import { GithubAuthModuleOptions } from '@nestjs-hybrid-auth/github';
import { TwitchAuthModuleOptions } from '@nestjs-hybrid-auth/twitch';

export interface HybridAuthModuleOptions {
google?: GoogleAuthModuleOptions;
Expand All @@ -16,6 +17,7 @@ export interface HybridAuthModuleOptions {
facebook?: FacebookAuthModuleOptions;
instagram?: InstagramAuthModuleOptions;
github?: GithubAuthModuleOptions;
twitch?: TwitchAuthModuleOptions;
}

export interface HybridAuthModuleAsyncOptions {
Expand Down Expand Up @@ -43,4 +45,8 @@ export interface HybridAuthModuleAsyncOptions {
IdentityModuleOptionsFactory<GithubAuthModuleOptions>,
GithubAuthModuleOptions
>;
twitch?: IdentityModuleAsyncOptions<
IdentityModuleOptionsFactory<TwitchAuthModuleOptions>,
TwitchAuthModuleOptions
>;
}
1 change: 1 addition & 0 deletions packages/nestjs-hybrid-auth-all/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ export * from '@nestjs-hybrid-auth/google';
export * from '@nestjs-hybrid-auth/github';
export * from '@nestjs-hybrid-auth/twitter';
export * from '@nestjs-hybrid-auth/linkedin';
export * from '@nestjs-hybrid-auth/twitch';
export * from './hybrid-auth.module';
export * from './hybrid-auth.options';
238 changes: 238 additions & 0 deletions packages/nestjs-hybrid-auth-twitch/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
# NestJS Twitch Authentication

Implement twitch authentication in your NestJS application.

## Install

```bash
npm install @nestjs-hybrid-auth/twitch --save
```

OR

```bash
yarn add @nestjs-hybrid-auth/twitch
```

### Note: **Twitch oauth applications require secure redirect urls. Please make sure to have https in your dev env also.**

## How To Use?

The package exports mainly a [dynamic module](https://docs.nestjs.com/fundamentals/dynamic-modules) and [guard](https://docs.nestjs.com/guards). The module should be imported in your app.module.ts and guards should be used on the route handlers of any controller.

## Example Code For app.module.ts

### Simple static configuration

Want to jump directly to the [available options](#twitchauthmoduleoptions)?

If you just want to provide the static values or have them handy, pass them as options to the `forRoot` static method like below. The options object is type of `TwitchAuthModuleOptions`.

```typescript
import { TwitchAuthModule } from '@nestjs-hybrid-auth/twitch';

@Module({
imports: [
TwitchAuthModule.forRoot({
clientID: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
callbackURL: process.env.CALLBACK_URL,
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
```

### `useFactory` to get the ConfigService injected.

If you want to make use of nest's [ConfigModule](https://docs.nestjs.com/techniques/configuration#installation) to get the auth configuration for a provider from `.env` config files, use `forRootAsync` static method. The options to this method are typeof `TwitchAuthModuleAsyncOptions` which accepts a `useFactory` property. `useFactory` is a function which gets the instances injected whatever has been provided in `inject` array. You can use those instances to prepare and return the actual `TwitchAuthModuleOptions` object. ConfigService can be one of them as per your choice.

```typescript
import { TwitchAuthModule } from '@nestjs-hybrid-auth/twitch';

@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
cache: true,
expandVariables: true,
}),
TwitchAuthModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (configService: ConfigService) => ({
clientID: configService.get('TWITCH_CLIENT_ID'),
clientSecret: configService.get('TWITCH_CLIENT_SECRET'),
callbackURL: configService.get('TWITCH_CALLBACK_URL'),
}),
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
```

### Use `useClass` to get your auth config from a class

If the `useFactory` makes your app module bloated with a lot of boilerplate code, you can `useClass` to provide an existing config provider class. The class must implement `TwitchAuthModuleOptionsFactory` interface and `createModuleOptions` method. This method should return `TwitchAuthModuleOptions` object. Similar to `useFactory`, whatever you provide in `inject` array, it will get injected in the constructor of your class. Follow the example:

**hybrid-auth.config.ts**

```typescript
import { ConfigService } from '@nestjs/config';
import {
TwitchAuthModuleOptions,
TwitchAuthModuleOptionsFactory,
} from '@nestjs-hybrid-auth/twitch';

@Injectable()
class HybridAuthConfig implements TwitchAuthModuleOptionsFactory {
constructor(private configService: ConfigService) {}

createModuleOptions(): TwitchAuthModuleOptions {
return {
clientKey: this.configService.get('TWITCH_CLIENT_ID'),
clientSecret: this.configService.get('TWITCH_CLIENT_SECRET'),
callbackURL: this.configService.get('TWITCH_CALLBACK_URL'),
};
}
}
```

**app.module.ts**

```typescript
import { TwitchAuthModule } from '@nestjs-hybrid-auth/twitch';

@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
cache: true,
expandVariables: true,
}),
TwitchAuthModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useClass: HybridAuthConfig,
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
```

## Example Code For Controller

Once you have setup the module properly in module file, its time to configure your route handlers to make the user properly redirected to appropriate identity provider's login page. `@nestjs-hybrid-auth/twitch` provides a guard and result interface to make it enabled.

Each route will have two variants. One is to redirect to social login page and the other is to collect the response such as access/refresh tokens and user profile etc. The result will be attached to `Request` object's `hybridAuthResult` property as shown in the example below.

### app.controller.ts

```typescript
import { UseTwitchAuth, TwitchAuthResult } from '@nestjs-hybrid-auth/twitch';

@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}

@UseTwitchAuth()
@Get('auth/twitch')
loginWithTwitch() {
return 'Login with Twitch';
}

@UseTwitchAuth()
@Get('auth/twitch-login/callback')
twitchCallback(@Request() req): Partial<TwitchAuthResult> {
const result: TwitchAuthResult = req.hybridAuthResult;
return {
accessToken: result.accessToken,
refreshToken: result.refreshToken,
profile: result.profile,
};
}
}
```

## Exports

`@nestjs-hybrid-auth/twitch` exports various decorators, interfaces and methods.

### UseTwitchAuth

`UseTwitchAuth` is NestJS `Guard` which hijacks your nest request and redirects users to the appropriate login page of your configured identity provider (twitch in this case). The same guard can be used on `callback` route also as shown in the example above. In the callback route handler, the `req: Request` object will have a property `hybridAuthResult` which is an object of type `TwitchAuthResult`.

```typescript
@UseTwitchAuth(options: TwitchAuthGuardOptions)
@Get('auth/twitch')
loginWithTwitch() {
return 'Login with Twitch';
}
```

### TwitchAuthGuardOptions

This is a simple object to be passed into `UseTwitchAuth` guard as shown in example above if you want to pass some extra parameters to query the twitch result. It can be left empty for default result.

### TwitchAuthModule

This is the dynamic module which must be imported in your app's main module with `forRoot` or `forRootAsync` static methods whichever suits your need. Both will return a [NestJS dynamic module](https://docs.nestjs.com/fundamentals/dynamic-modules).

```typescript
interface TwitchAuthModule {
forRoot(options: TwitchAuthModuleOptions): DynamicModule;
forRootAsync(options: TwitchAuthModuleAsyncOptions): DynamicModule;
}
```

### TwitchAuthModuleOptions

If you are configuring your module with `forRoot` static method, pass in the module options given below. They can be called the twitch passport strategy options also.

```typescript
interface TwitchAuthModuleOptions {
clientID: string;
clientSecret: string;
callbackURL: string;
scope?: string[] | undefined;
}
```

### TwitchAuthModuleAsyncOptions

If you want to configure the `TwitchAuthModule` dynamically having the config or other services injected, pass in async options in the `forRootAsync` static method. Please refer to the example above for `useFactory` and `useClass` properties.

```typescript
interface TwitchAuthModuleAsyncOptions {
useExisting?: Type<TwitchAuthModuleOptionsFactory>;
useClass?: Type<TwitchAuthModuleOptionsFactory>;
useFactory?: (
...args: any[]
) => Promise<TwitchAuthModuleOptions> | TwitchAuthModuleOptions;
inject?: any[];
}
```

### TwitchAuthModuleOptionsFactory

```typescript
interface TwitchAuthModuleOptionsFactory {
createModuleOptions():
| Promise<TwitchAuthModuleOptions>
| TwitchAuthModuleOptions;
}
```

## Have Issues?

If you still have trouble setting up the workflow properly, please file an issue at [Issues](https://github.com/mjangir/nestjs-hybrid-auth/issues) page.

## Maintainers

[Manish Jangir](https://github.com/mjangir)
51 changes: 51 additions & 0 deletions packages/nestjs-hybrid-auth-twitch/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"name": "@nestjs-hybrid-auth/twitch",
"description": "NestJS twitch authentication using passport",
"version": "0.0.0",
"license": "MIT",
"author": "Manish Jangir <[email protected]> (https://manishjangir.com)",
"repository": {
"type": "git",
"url": "git+https://github.com/mjangir/nestjs-hybrid-auth.git"
},
"bugs": {
"url": "https://github.com/mjangir/nestjs-hybrid-auth/issues"
},
"homepage": "https://nestjs-hybrid-auth.manishjangir.com",
"keywords": [
"nestjs twitch",
"nestjs passport",
"nestjs twitch login",
"twitch auth",
"nestjs twitch auth",
"nestjs login",
"nestjs social login"
],
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"files": [
"dist"
],
"peerDependencies": {
"@nestjs/common": "^8.0.0",
"@nestjs/core": "^8.0.0",
"@nestjs/passport": "^8.0.0",
"passport": "^0.5.0",
"reflect-metadata": "^0.1.13"
},
"scripts": {
"test": "tsdx test --env=jsdom",
"test:watch": "npm run test -- --watchAll",
"start": "tsdx watch --target node --tsconfig tsconfig.build.json --verbose --noClean",
"build": "tsdx build --target node --tsconfig tsconfig.build.json",
"lint": "tsdx lint src",
"prepublish": "npm run build"
},
"dependencies": {
"@nestjs-hybrid-auth/core": "^1.0.0",
"@types/lodash": "^4.14.174",
"@types/passport-twitch-latest": "^1.0.1",
"lodash": "^4.17.21",
"passport-twitch-latest": "^1.0.0"
}
}
8 changes: 8 additions & 0 deletions packages/nestjs-hybrid-auth-twitch/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export { UseTwitchAuth } from './twitch.guard';
export { TwitchAuthModule } from './twitch.module';
export {
TwitchAuthModuleOptions,
TwitchAuthModuleOptionsFactory,
TwitchAuthGuardOptions,
TwitchAuthResult,
} from './twitch.types';
1 change: 1 addition & 0 deletions packages/nestjs-hybrid-auth-twitch/src/twitch.constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const TWITCH_HYBRID_AUTH_OPTIONS = 'TWITCH_HYBRID_AUTH_OPTIONS';
Loading

0 comments on commit 3400250

Please sign in to comment.