Giter VIP home page Giter VIP logo

Comments (34)

nielsmeima avatar nielsmeima commented on May 5, 2024 28

Sorry for the super late response! Had a busy start of the semester...

I have a working template for social providers via passport (Google, Github, etc.) which I use as a SSO option on an Angular application, in which further authentication is handled by using JWTs as described in the current Nest.js docs. I will try to at least show some material here!

I am also debating on writing a Medium post about Angular + OAuth2 SSO + Passport + JWTs + Nest.js. People interested in that?

from docs.nestjs.com.

nielsmeima avatar nielsmeima commented on May 5, 2024 11

The back-end part is live: https://medium.com/@nielsmeima/auth-in-nest-js-and-angular-463525b6e071

from docs.nestjs.com.

johnbiundo avatar johnbiundo commented on May 5, 2024 8

For those following/landing on this thread, there are a couple of recent documents that should help

  1. The authentication chapter was completely re-written (as of July 2019) with an end-to-end example
  2. A recent (July 2019) article on dev.to on sessions covers sessions and was reviewed by the Nest core team.

I'll keep this open for now as I'm not sure #1365, and #264 are addressed yet.

from docs.nestjs.com.

sdoxsee avatar sdoxsee commented on May 5, 2024 7

I'm new to Node.js, let alone NestJS, but, thanks to others who have added their samples here, I've published a blog post and GitHub repo to demonstrate OpenID Connect (OIDC) authentication using the OpenID Certified™ passport strategy "openid-client". I'm using Google as my Identity Provider but it works equally well with Keycloak, Okta, etc. I serve up a create-react-app-typescript front-end leveraging the session from express-session and storing the session in MongoDB.

from docs.nestjs.com.

MrXploder avatar MrXploder commented on May 5, 2024 6

I'm trying to get openid-client to work with nestjs/passport..... since, PassportStrategy needs in the constructor the full params (client_id, client_secret) but openid-client needs to call an "Issuer" promise first (discover)/method.... so anyone could please give some advices to continue.

  1. first aproach: returns "client" must be an instance of "openid-client"
import { Injectable } from "@nestjs/common";
import { PassportStrategy } from "@nestjs/passport";
import * as OpenIDClient from "openid-client"

@Injectable()
export class OpenIDStrategy extends PassportStrategy(OpenIDClient.Strategy, 'oidc') {
  constructor() {
    super({
      client_id: process.env.APP_AUTH_CLIENT_ID,
      client_secret: process.env.AUTH_CLIENT_SECRET,
      callbackURL: process.env.APP_AUTH_REDIRECT_URI,
      passReqToCallback: true,
      scope: [
        'openid',
      ]
    })
  }

  validate(request: any, accessToken: string, refreshToken: string, profile, done: Function) {
    return done(null, { ...profile, accessToken, refreshToken });
  }
}
  1. Here's what I've got in express (currently working code)...
(...)
try {
    trustIssuer = await OpenIDIssuer.discover(process.env.AUTH);

    const client = new trustIssuer.Client({
      client_id: process.env.APPLICATION_UID,
      client_secret: process.env.SECRET
    });

    client.CLOCK_TOLERANCE = 10000000;

    const params = {
      redirect_uri: `${process.env.REDIRECT_URI}/auth/cb`,
      scope: process.env.SCOPES
    }

    passport.use('oidc', new OpenIDStrategy({ client, params, passReqToCallback: false, usePKCE: false }, (tokenset, userinfo, done) => {
      tokenset.created_at = moment().format("X");
      tokenset.expires_at = moment().add(tokenset.expires_in - 72, "seconds").format("X");
      return done(null, { ...userinfo, ...tokenset });
    }));

    passport.serializeUser((userData, done) => {
      if (userData) {
        done(null, userData);
      } else {
        done("error", false);
      }
    });

    passport.deserializeUser((userData, done) => {
      if (userData) {
        done(null, userData);
      } else {
        done("error", false);
      }
    });
(...)

from docs.nestjs.com.

weeco avatar weeco commented on May 5, 2024 3

@nielsmeima Any update on this?

from docs.nestjs.com.

TrejGun avatar TrejGun commented on May 5, 2024 3

Guys here is what you are looking for - boilerplate with local/google auth, sessions, and ACL
https://www.gemunion.io/documentation/authorization
https://github.com/GemunIon/nestjs-auth/

@johnbiundo thanks for you code, I used it as starting point

from docs.nestjs.com.

hegelstad avatar hegelstad commented on May 5, 2024 3

Here is my example which works great!

// src/auth/auth.service.ts
import { Injectable } from '@nestjs/common';
import { PhotonService } from '../services/photon.service';
import { User } from '@generated/photon';

@Injectable()
export class AuthService {
  constructor(private readonly photon: PhotonService) {}

  async validateUser(subject: string): Promise<User> {
    const user = await this.photon.users.findOne({ where: { id: subject } });
    if (user) return user;
    return null;
  }

  async createValidatedUser(subject, email, firstname, lastname): Promise<User> {
    const user = await this.photon.users.create({
      data: {
        id: subject,
        email,
        firstname,
        lastname,
        role: 'USER',
      }
    });
    if (user) return user;
    return null;
  }
}
// src/auth/onelogin.strategy.ts
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { Strategy, Client, UserinfoResponse, TokenSet, Issuer } from 'openid-client';
import { User } from '@generated/photon';
import { AuthService } from './auth.service';

export const buildOpenIdClient = async () => {
  const TrustIssuer = await Issuer.discover(`https://${process.env.AUTH_ONELOGIN_SUBDOMAIN}.onelogin.com/oidc/.well-known/openid-configuration`);
  const client = new TrustIssuer.Client({
    client_id: process.env.AUTH_ONELOGIN_CLIENT_ID,
    client_secret: process.env.AUTH_ONELOGIN_CLIENT_SECRET,
    token_endpoint_auth_method: 'client_secret_post',
  });
  return client;
};

@Injectable()
export class OneloginStrategy extends PassportStrategy(Strategy, 'onelogin') {
  client: Client;

  constructor(private readonly authService: AuthService, client: Client) {
    super({
      client: client,
      params: {
        redirect_uri: process.env.AUTH_ONELOGIN_REDIRECT_URI,
        scope: process.env.AUTH_ONELOGIN_SCOPE,
      },
      passReqToCallback: false,
      usePKCE: false,
    });

    this.client = client;
  }

  async validate(tokenset: TokenSet): Promise<User> {
    const userinfo: UserinfoResponse = await this.client.userinfo(tokenset);

    try {
      const user = await this.authService.validateUser(userinfo.sub);
      if (user) return user;
      return await this.authService.createValidatedUser(
        userinfo.sub, userinfo.email, userinfo.given_name, userinfo.family_name
      );
    } catch (err) {
      throw new UnauthorizedException();
    }
  }
}
// src/auth/auth.module.ts
import { Module } from '@nestjs/common';
import { PassportModule } from '@nestjs/passport';
import { PhotonService } from '../services/photon.service';
import { LoginGuard } from '../guards/login.guard';
import { SessionGuard } from '../guards/session.guard';
import { AuthenticatedGuard } from '../guards/authenticated.guard';
import { GqlAuthGuard } from '../guards/gql-auth.guard';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { OneloginStrategy, buildOpenIdClient } from './onelogin.strategy';
import { SessionSerializer } from './session.serializer';

const OneloginStrategyFactory = {
  provide: 'OneloginStrategy',
  useFactory: async (authService: AuthService) => {
    const client = await buildOpenIdClient(); // secret sauce! build the dynamic client before injecting it into the strategy for use in the constructor super call.
    const strategy = new OneloginStrategy(authService, client);
    return strategy;
  },
  inject: [AuthService]
};

@Module({
  imports: [
    PassportModule.register({ session: true, defaultStrategy: 'onelogin' }),
  ],
  controllers: [AuthController],
  providers: [
    PhotonService,
    LoginGuard,
    SessionGuard,
    AuthenticatedGuard,
    GqlAuthGuard,
    AuthService,
    OneloginStrategyFactory,
    SessionSerializer,
  ],
  exports: [GqlAuthGuard, SessionGuard, LoginGuard, AuthenticatedGuard, AuthService],
})
export class AuthModule {}
// src/auth/auth.controller.ts
import { Controller, Get, UseGuards, Request } from '@nestjs/common';
import { User } from '@generated/photon';
import { CurrentUser } from '../users/user.decorator';
import { LoginGuard } from '../guards/login.guard';
import { SessionGuard } from '../guards/session.guard';
import { AuthenticatedGuard } from '../guards/authenticated.guard';

@Controller('auth')
export class AuthController {
  @UseGuards(LoginGuard)
  @Get('onelogin/login')
  public async login() {
  }

  @UseGuards(LoginGuard)
  @Get('onelogin/callback')
  public async callback(@Request() req) {
    return req.user;
  }

  @UseGuards(AuthenticatedGuard)
  @Get('secret')
  getTheSecret(@Request() req) {
    return 'a secret has been returned';
  }
}
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app/app.module';
import { ValidationPipe } from '@nestjs/common';
import * as session from 'express-session';
import * as passport from 'passport';
require('dotenv').config();

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  // Validation
  app.useGlobalPipes(new ValidationPipe());

  // Authentication & Session
  app.use(session({
    secret: process.env.AUTH_SESSION_SECRET,
    resave: false,
    saveUninitialized: false,
    cookie{
      httpOnly: true
    }
  }));
  app.use(passport.initialize());
  app.use(passport.session());

  // Cors
  app.enableCors();

  await app.listen(3000);
}

bootstrap();

from docs.nestjs.com.

sbrannstrom avatar sbrannstrom commented on May 5, 2024 3

Here's a working example of LDAP authentication in NestJS that I've created for those looking for one:
https://github.com/sbrannstrom/nestjs-passport-ldap-example

from docs.nestjs.com.

MrXploder avatar MrXploder commented on May 5, 2024 2

@ides15 Unfortunately, no. I ended up doing all this "openid strategy registration" in the bootstrapping section. Then I made a Module(Auth)->Controller to register the 2 routes that I need (auth and auth/cb) and a custom middleware to wrap the passport middleware.

Here is my full working code (sorry for my very bad English):
(all paths are related to "src/*" proyect)

/main.ts file to register redis and passport configuration

import * as dotenv from "dotenv";

import * as path from "path";
import * as moment from "moment";
import * as session from "express-session";
import * as redis from "redis";
import * as redisConnect from "connect-redis";
import * as passport from "passport";
import * as bluebirdPromise from "bluebird";

dotenv.config({ path: path.resolve(process.env.PWD, "..", ".env") });
global.Promise = bluebirdPromise;

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { Issuer as OpenIDIssuer, Strategy as OpenIDStrategy } from "openid-client";

// OpenIDIssuer.defaultHttpOptions.timeout = 10000;
const redisStore = redisConnect(session);
const redisClient = redis.createClient({port: process.env.REDIS_PORT, host: process.env.REDIS_HOST});

function redisHandle() {
  return new Promise((resolve, reject) => {
    redisClient.on("error", err => {
      console.error("Redis connection error");
      reject(err);
    });

    redisClient.on("ready", (err, res) => {
      if (!err) {
        console.info("Redis connection successful");
        resolve(res);
      } else {
        reject(err);
      }
    })
  })
}

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  await redisHandle();

  const TrustIssuer = await OpenIDIssuer.discover(process.env.SERVICE_URI_AUTH);

  const params = {
    redirect_uri: process.env.APP_AUTH_REDIRECT_URI,
    scope: process.env.APP_AUTH_SCOPES,
  }

  const client = new TrustIssuer.Client({
    client_id: process.env.APP_AUTH_CLIENT_ID,
    client_secret: process.env.APP_AUTH_CLIENT_SECRET,
  });

  client.CLOCK_TOLERANCE = Infinity;

  passport.use('oidc', new OpenIDStrategy({ client, params, passReqToCallback: false, usePKCE: false }, (tokenset, userinfo, done) => {
    tokenset.created_at = moment().format("X");
    tokenset.expires_at = moment().add(tokenset.expires_in - 72, "seconds").format("X");
    return done(null, { ...userinfo, ...tokenset });
  }));

  passport.serializeUser((userData, done) => {
    if (userData) {
      done(null, userData);
    } else {
      done("error", false);
    }
  });

  passport.deserializeUser((userData, done) => {
    if (userData) {
      done(null, userData);
    } else {
      done("error", false);
    }
  });

  app.use(passport.initialize());
  app.use(passport.session());

  app.use(session({
    name: "app.sid",
    secret: 'qwerty',
    resave: true,
    unset: "destroy",
    saveUninitialized: true,
    cookie: {
      secure: false,
    },
    store: new redisStore({
      host: process.env.REDIS_HOST,
      port: process.env.REDIS_PORT,
      client: redisClient
    })
  }));

  await app.listen(3000);
}

bootstrap();

/auth/auth.module.ts to assign my 2 custom middlewares to the 2 openid routes

import { Module, NestModule, MiddlewareConsumer, RequestMethod } from '@nestjs/common';
import { AuthController } from './auth.controller';
import { PassportAuthMiddleware, PassportCallbackMiddleware } from "../common/middleware/passport";

@Module({
  controllers: [
    AuthController
  ],
  providers: []
})
export class AuthModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(PassportAuthMiddleware)
      .forRoutes({ path: "/auth", method: RequestMethod.GET })

    consumer
      .apply(PassportCallbackMiddleware)
      .forRoutes({ path: "/auth/cb", method: RequestMethod.GET })
  }
}

/auth/auth.controller.ts to register the 2 routes

import { Controller, Get } from '@nestjs/common';

@Controller('auth')
export class AuthController {
  @Get("")
  oidcLogin() { }

  @Get("cb")
  oidcCallback() { }
}

and finally /common/middleware/passport.ts to wrap 2 passport middlwares

import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response } from 'express';

import * as passport from "passport"

@Injectable()
export class PassportAuthMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: Function) {
    return passport.authenticate("oidc")(req, res, next);
  }
}

@Injectable()
export class PassportCallbackMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: Function) {
    return passport.authenticate("oidc", { successRedirect: "/", failureRedirect: "/auth" })(req, res, next);
  }
}

if you guys have a better approach, let me know! :D

from docs.nestjs.com.

hegelstad avatar hegelstad commented on May 5, 2024 2

@sbrannstrom I think you should create a ParamService and inject that into OneloginStrategyFactory similarly to how AuthService is injected to the Factory. Then it will be available in the factory and you can pass it into OneloginStrategy and have it available in the super call.

I am not aware if it supports to be changed when running, but if not you should consider just creating two separate instances.

@sdoxsee please credit me somehow in the blog post, doesn't have to be a big thing, and I would really appreciate it :D

from docs.nestjs.com.

TBG-FR avatar TBG-FR commented on May 5, 2024 2

If you're struggling with NestJS, Passport and OIDC (openid-client), this article might help you (looks like its author used this thread to make it !) : https://sdoxsee.github.io/blog/2020/02/05/cats-nest-nestjs-mongo-oidc.html (related repo)

from docs.nestjs.com.

weeco avatar weeco commented on May 5, 2024 1

Sure I am interested! Please make sure to publish anything at all. I often read such comments and in the end I never hear or see any piece of code because the author lost interest or time to publish it.

from docs.nestjs.com.

nielsmeima avatar nielsmeima commented on May 5, 2024 1

I know, have seen a lot of the same thing. I actually have some time tonight, so I will get right to it.

from docs.nestjs.com.

abouroubi avatar abouroubi commented on May 5, 2024 1

Hello,

I just published a sample app that uses JWT short lived access tokens, and long lived refresh tokens.
My initial goal is to have a secure auth sample based on NestJS, that will be used by a mobile app.

The refresh token has a sliding expiration time, so if the user uses the mobile app frequently he never get disconnected.

Also, when the user is logged in, we save the client id, it can be the smartphone name or something meaningful for the user, this client id is associated to the refresh token, this gives the possibility to the user to disconnect only from one device and stay connected in the other devices, he can also disconnect from all the devices.

Here is the repo : https://github.com/abouroubi/nestjs-auth-jwt

Comments, PR's are welcome.

from docs.nestjs.com.

ides15 avatar ides15 commented on May 5, 2024 1

@MrXploder have you figured out the "client" must be an instance of "openid-client" issue?

from docs.nestjs.com.

Roms1383 avatar Roms1383 commented on May 5, 2024 1

I made a sample repository here in case it might help some people :)
I use Nest.js, nestjs/passport with a local strategy and url redirection.
Detailed article here.

from docs.nestjs.com.

MrXploder avatar MrXploder commented on May 5, 2024 1

thanks @Roms1383, your solution works perfect for me. This should be in the official docs

from docs.nestjs.com.

sdoxsee avatar sdoxsee commented on May 5, 2024 1

Hey @hegelstad. I had a link to your comment in my blog references but added your name (and GitHub profile) in this commit. Please create a PR if you'd prefer something else. Thanks again for your helpful comment!

from docs.nestjs.com.

nielsmeima avatar nielsmeima commented on May 5, 2024

I coded up canonical Nest.js examples for Local, Google and Github (other OAuths are nearly identical ofcourse) strategies. I would be very happy to share these and possibly use them to improve the docs. How can I help?

from docs.nestjs.com.

kamilmysliwiec avatar kamilmysliwiec commented on May 5, 2024

Wow, that is a great news @nielsmeima! Feel free to create a PR if you have some spare time 🙂

from docs.nestjs.com.

Offlein avatar Offlein commented on May 5, 2024

@nielsmeima Hello, I just wanted to throw in my support for at least seeing your examples, even if they're ugly!

from docs.nestjs.com.

nielsmeima avatar nielsmeima commented on May 5, 2024

Spend the whole evening on writing the back-end part of the Medium post. Code is embedded using JSFiddles and I will also put it on Github.

I will do the front-end (Angular) part tomorrow and will probably also post it then.

from docs.nestjs.com.

weeco avatar weeco commented on May 5, 2024

Thanks for the article, going to read it tonight! Maybe @kamilmysliwiec or @BrunnerLivio wanna take a look at it. You could share it at twitter and mention https://twitter.com/nestframework?lang=en .

from docs.nestjs.com.

nielsmeima avatar nielsmeima commented on May 5, 2024

Yes, will share it on Twitter today. What did you think about the article?

from docs.nestjs.com.

mfechner avatar mfechner commented on May 5, 2024

The back-end part is live: https://medium.com/@nielsmeima/auth-in-nest-js-and-angular-463525b6e071

The article is really great and answers some unanswered questions.
Thanks a lot for it.
Would be great if it could be linked in the nestjs documentation?

from docs.nestjs.com.

synapdk avatar synapdk commented on May 5, 2024

I attempted to follow the Inheritance section of https://docs.nestjs.com/techniques/authentication to implement a local + session AuthGuard, but had to make some adjustments:

import { ExecutionContext, Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
export class LocalAuthGuard extends AuthGuard('local')
{
	async canActivate(context: ExecutionContext): Promise<boolean>
	{
		const request = context.switchToHttp().getRequest();

		// End any current session
		await request.logout();

		// Super canActivate() must establish an authenticated user before logIn() will work
		const can:boolean = await super.canActivate(context) as boolean;

		if (can) {
			// Establish a logIn session (https://github.com/nestjs/passport/issues/7)
			await super.logIn(request);
		}

		return can;
	}

	handleRequest(err, user, info) {
		return super.handleRequest(err, user, info);
	}
}

It doesn't feel like that's exactly what was intended, but does work in my case.

from docs.nestjs.com.

Offlein avatar Offlein commented on May 5, 2024

@synapdk Thanks for sharing -- I've been trying to get Sessions working with Google OAuth2 for a long time. I have been busy with another project but am getting back into this.

Are you saying that you have a local username+password login that doesn't re-prompt because of a cookie-based session effectively working right now with just the pasted code?

I've gone through so many of these tutorials that ignore NestJS best-practices, or offer conflicting information about what to do. It seems like if you're using Sessions, according to that authentication page you linked to, you just set { session: true} as the parameter when doing PassportModule.register(...), but other places say I need to turn on Passport sessions (or even Express sessions?) at the root of the app in main.ts.

I'm trying to do this with the passport-google-oauth-20 module, but no matter what, any time I hit a controller action guarded by the "googleAuthGuard" guard that I made (implementing that strategy) I get the Google prompt anyway.

I don't even see any cookies for my application in this case.

Thanks for any help!

from docs.nestjs.com.

sbrannstrom avatar sbrannstrom commented on May 5, 2024

I'm using @hegelstad 's example which is working great but I'm having one issue. My OpenID Connect provider can provide several different authentication methods, the only thing I need to do is to alter one of the params in the constructors super-call.
For example, params.acr_values: 'urn:signicat:oidc:method:sbid' to params.acr_values: 'urn:signicat:oidc:method:siths' in order to switch from Swedish BankID to Swedish doctors SITHS-card login.

My question is, is it possible to alter this after the application has been started?

I'm currently using dotenv to have the variables read from a file. I've tried updating the variable from an endpoint but it does not change acr_value due to it being loaded at startup and it doesn't seem to work to update it after startup of the backend.

I'd appreciate any and all help :)

from docs.nestjs.com.

sbrannstrom avatar sbrannstrom commented on May 5, 2024

@hegelstad thank you kindly, I'll give that a try :)

from docs.nestjs.com.

sbrannstrom avatar sbrannstrom commented on May 5, 2024

@sbrannstrom I think you should create a ParamService and inject that into OneloginStrategyFactory similarly to how AuthService is injected to the Factory. Then it will be available in the factory and you can pass it into OneloginStrategy and have it available in the super call.

I am not aware if it supports to be changed when running, but if not you should consider just creating two separate instances.

I tried updating the acr_values with dotenv (process.env.ACR_VALUES = 'foobar') but it does not seem to update after the backend has been started.... It seems like a lot of extra code to create separate instances for each auth method since we will use multiple (5+ and even more in the future).... So if anyone has any more ideas, I'd greatly appreciate it :) Thanks.

from docs.nestjs.com.

brianpooe avatar brianpooe commented on May 5, 2024

Here is my example which works great!

// src/auth/auth.service.ts
import { Injectable } from '@nestjs/common';
import { PhotonService } from '../services/photon.service';
import { User } from '@generated/photon';

@Injectable()
export class AuthService {
  constructor(private readonly photon: PhotonService) {}

  async validateUser(subject: string): Promise<User> {
    const user = await this.photon.users.findOne({ where: { id: subject } });
    if (user) return user;
    return null;
  }

  async createValidatedUser(subject, email, firstname, lastname): Promise<User> {
    const user = await this.photon.users.create({
      data: {
        id: subject,
        email,
        firstname,
        lastname,
        role: 'USER',
      }
    });
    if (user) return user;
    return null;
  }
}
// src/auth/onelogin.strategy.ts
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { Strategy, Client, UserinfoResponse, TokenSet, Issuer } from 'openid-client';
import { User } from '@generated/photon';
import { AuthService } from './auth.service';

export const buildOpenIdClient = async () => {
  const TrustIssuer = await Issuer.discover(`https://${process.env.AUTH_ONELOGIN_SUBDOMAIN}.onelogin.com/oidc/.well-known/openid-configuration`);
  const client = new TrustIssuer.Client({
    client_id: process.env.AUTH_ONELOGIN_CLIENT_ID,
    client_secret: process.env.AUTH_ONELOGIN_CLIENT_SECRET,
    token_endpoint_auth_method: 'client_secret_post',
  });
  return client;
};

@Injectable()
export class OneloginStrategy extends PassportStrategy(Strategy, 'onelogin') {
  client: Client;

  constructor(private readonly authService: AuthService, client: Client) {
    super({
      client: client,
      params: {
        redirect_uri: process.env.AUTH_ONELOGIN_REDIRECT_URI,
        scope: process.env.AUTH_ONELOGIN_SCOPE,
      },
      passReqToCallback: false,
      usePKCE: false,
    });

    this.client = client;
  }

  async validate(tokenset: TokenSet): Promise<User> {
    const userinfo: UserinfoResponse = await this.client.userinfo(tokenset);

    try {
      const user = await this.authService.validateUser(userinfo.sub);
      if (user) return user;
      return await this.authService.createValidatedUser(
        userinfo.sub, userinfo.email, userinfo.given_name, userinfo.family_name
      );
    } catch (err) {
      throw new UnauthorizedException();
    }
  }
}
// src/auth/auth.module.ts
import { Module } from '@nestjs/common';
import { PassportModule } from '@nestjs/passport';
import { PhotonService } from '../services/photon.service';
import { LoginGuard } from '../guards/login.guard';
import { SessionGuard } from '../guards/session.guard';
import { AuthenticatedGuard } from '../guards/authenticated.guard';
import { GqlAuthGuard } from '../guards/gql-auth.guard';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { OneloginStrategy, buildOpenIdClient } from './onelogin.strategy';
import { SessionSerializer } from './session.serializer';

const OneloginStrategyFactory = {
  provide: 'OneloginStrategy',
  useFactory: async (authService: AuthService) => {
    const client = await buildOpenIdClient(); // secret sauce! build the dynamic client before injecting it into the strategy for use in the constructor super call.
    const strategy = new OneloginStrategy(authService, client);
    return strategy;
  },
  inject: [AuthService]
};

@Module({
  imports: [
    PassportModule.register({ session: true, defaultStrategy: 'onelogin' }),
  ],
  controllers: [AuthController],
  providers: [
    PhotonService,
    LoginGuard,
    SessionGuard,
    AuthenticatedGuard,
    GqlAuthGuard,
    AuthService,
    OneloginStrategyFactory,
    SessionSerializer,
  ],
  exports: [GqlAuthGuard, SessionGuard, LoginGuard, AuthenticatedGuard, AuthService],
})
export class AuthModule {}
// src/auth/auth.controller.ts
import { Controller, Get, UseGuards, Request } from '@nestjs/common';
import { User } from '@generated/photon';
import { CurrentUser } from '../users/user.decorator';
import { LoginGuard } from '../guards/login.guard';
import { SessionGuard } from '../guards/session.guard';
import { AuthenticatedGuard } from '../guards/authenticated.guard';

@Controller('auth')
export class AuthController {
  @UseGuards(LoginGuard)
  @Get('onelogin/login')
  public async login() {
  }

  @UseGuards(LoginGuard)
  @Get('onelogin/callback')
  public async callback(@Request() req) {
    return req.user;
  }

  @UseGuards(AuthenticatedGuard)
  @Get('secret')
  getTheSecret(@Request() req) {
    return 'a secret has been returned';
  }
}
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app/app.module';
import { ValidationPipe } from '@nestjs/common';
import * as session from 'express-session';
import * as passport from 'passport';
require('dotenv').config();

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  // Validation
  app.useGlobalPipes(new ValidationPipe());

  // Authentication & Session
  app.use(session({
    secret: process.env.AUTH_SESSION_SECRET,
    resave: false,
    saveUninitialized: false,
    cookie{
      httpOnly: true
    }
  }));
  app.use(passport.initialize());
  app.use(passport.session());

  // Cors
  app.enableCors();

  await app.listen(3000);
}

bootstrap();

This does not work for me unfortunately, the callback endpoint returns 500 or 401 sometimes even though I was redirected to the login screen successfully but going back to my app is where the issue lies.. it bombs out on super.canActivate(context). Any help would be appreciated thanks.

from docs.nestjs.com.

xyide avatar xyide commented on May 5, 2024

This does not work for me unfortunately, the callback endpoint returns 500 or 401 sometimes even though I was redirected to the login screen successfully but going back to my app is where the issue lies.. it bombs out on super.canActivate(context). Any help would be appreciated thanks.

Are you sure that your validate method in your OneLoginStrategy class is executing properly and returning a user object to attach to the request?

from docs.nestjs.com.

dlopatin-v avatar dlopatin-v commented on May 5, 2024

How can I use IdentityServer4 GrandType.ClientCredential? We have only clientId, clientSecret and scopes(optional), there is no user.

from docs.nestjs.com.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.