-
Notifications
You must be signed in to change notification settings - Fork 117
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Extending PassportStrategy
does not take provider specific OAuth2 options in account
#57
Comments
Having the same issue with hd param for google, can you show the usage of a workaround? UPDATE: |
@iveselin just try with: @Injectable()
export class GoogleStrategy extends PassportStrategy(Strategy, 'google') {
// ...
// WILL be passed to authenticate()
authorizationParams(options: any): any {
return Object.assign(options, {
hd: '<HD_PARAM>'
});
}
} The problem is, that non-standard params passed through constructor |
While that does work it skips the validation and sanitization the original function does. You're able to achieve the same by overriding the authenticate function and it retains all original behavior of the strategy.
Edit: You can achieve similar results by calling super.authorizationParams, however if anyone stumbled upon custom_params or custom parameters this would be the way it'd work across providers (potentially). |
Can someone point me at the line of code that strips those options in |
@kamilmysliwiec It's not that It may be specific to Google's passport but they never take the options that are passed into the constructor, so it forces us to override the authenticate function to be able to handle Google. Here is the offending line. Edit: Additionally the core OAuth2 strategy does not merge the options from the constructor https://github.com/jaredhanson/passport-oauth2/blob/master/lib/strategy.js#L130 |
https://github.com/nestjs/passport/blob/master/lib/auth.guard.ts#L82 We pass PassportModule.register({
approval_prompt: 'force',
access_type: 'offline',
}) |
Which works for a single auth provider but if you wanted to support Microsoft and Google and they have a collision in auth params you'd run into issues. How would you support multiple passport providers? Assuming using auth0 isn't available. Is the correct solution to create a passport module per auth provider? How would you correctly set up the AuthGuard in this instance? |
Ahhh, got you now. That might be tricky. You could theoretically extend the guard and strip |
Or provide them with a parameter in the strategy of the provider? That's why I added the PR, so it can be done closest to where it is actually necessary. |
@kamilmysliwiec Should the strategy just override the authenticate function instead? I'm not sure how the auth guard would specifically solve this problem. An example with our projects would have multiple ways to authenticate with google/microsoft/facebook but sign our own jwt's with our own info. So we never use the google/microsoft/facebook auth guards as that's just for signup and login. We have it working by just overriding the authenticate function but that requires specifically to be careful that @nestjs/passport doesn't change how authenticate is called (not that it would) whereas passing in a customParameters would allow this project to manage the plumbing and simplify the consumer strategies |
@csidell-earny I tried your PR with my app. Identical code with only swapping in/out your version of I can't make this repo public, but if it matters, I could see if I can strip it down. But it looks like it has to do with the fact that I'm also using Passport sessions, a la stuff like this in my main.ts: app.use(session(...
app.use(passport.session())
|
Odd... Yeah I might need a minimum repo to debug that one. I don't see why it'd affect sessions. |
That will take some work. Let me see if I can figure out how to do it... |
I'd imagine you could just create a skeleton project with the cli and just copy over some hardcoded auth stuff. Don't need to integrate with everything. |
Yeah. It's integrated with a client side Angular app. A few tricky things to unwind. The app actually manages the authentication UI (Google Auth Popup, etc.) from the client side instead of a regular redirect. Shouldn't be too bad. I hope. 😄 |
Here ya go |
@johnbiundo Using your repo and swapping out for my library I can't get it to fail as you've explained. As far as I can tell from your repo it works, I get the serialized user just fine. I did have to remove a null reference to the usersModule in your AuthModule though. (Nothing else references it) |
@Sieabah I may not have a chance to try this again for a week or two. Currently on jury duty. |
OK, I had a chance to try this again. Still getting the same error. Here's what I do:
|
I updated my repo with those changes, and it also removed default install of @nestjs/passport, and includes the callback URL that should work for localhost. |
Alright I'll take another look this weekend. |
thank you @johnbiundo you saved my day! |
@ruslanguns Great! Glad it helped! |
I am not sure why we need a separate method just as @kamilmysliwiec said. The above suggestion makes perfect sense to me. I have Google, Twitter & Facebook working altogether with separate Strategy & Guards. An authenticate method with Also, just to be clear, according to approval_prompt: 'force', // should be "approvalPrompt" - strategy.js#L183
access_type: 'offline', // should be "accessType" - strategy.js#L138 CC: @johnbiundo |
I'm late to the party and not sure if this is something that has been supported with newer versions since you asked the question, but to provide specific options to the the passport For example, I had to create an auth guard for Facebook that would make sure that
With that, authTpe is always processed by passport's |
Just want to say I'm at the end of 5 hours of debugging tracking this down. insane! Can we at least document this behaviour for the future? |
I could do it like that: @Module({
imports: [
PassportModule.register({
accessType: 'offline',
prompt: 'consent',
}),
],
controllers: [...],
})
export class AuthModule {} |
Same happens for me when using the local strategy: import { Strategy } from 'passport-local';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { AuthService } from './auth.service';
@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
constructor(private authService: AuthService) {
super({ usernameField: 'email', passwordField: 'password' });
}
async validate(username: string, password: string): Promise<any> {
let email = username;
const user = await this.authService.validateUser(email, password);
if (!user) {
throw new UnauthorizedException();
}
return user;
}
} The passport strategy mixin class seems to get no arguments in the constructor So the options never get set: Maybe related to https://stackoverflow.com/a/36204599/483616? |
Ok, for me it looks like some sort of build cache. Basically stopping and restarting the watch wasn't enough, I had to run |
According to Facebook docs for api 2.4 (current is 3.2)
https://developers.facebook.com/blog/post/2015/07/08/graph-api-v2.4/ This means even if I use profileFields: ["id", "email", "name"], I still have to pass additional params to passport.use(new FacebookStrategy({
clientID: "CLIENT_ID",
clientSecret: "CLIENT_SECRET",
callbackURL: "http://www.example.com/auth/facebook/callback"
passReqToCallback : true,
profileFields: ["id", "emails", "name"]
})
);
app.get("/connect/facebook", passport.authorize("facebook", { scope : ["email"] })); Thanks to @csidell-earny his workaround works fine for me authenticate(req: Request, options?: Record<string, any>): void {
super.authenticate(req, Object.assign(options, {
scope: ["email"]
}));
} |
thanks for this, I can't believe this issue still exists. |
I'm submitting a...
Current behavior
Extending
PassportStrategy
does not work as expected. In case of extendingPassportStrategy(Strategy, 'google')
additional OAuth2 options, respectively provider specific options like e.g.approval_prompt
passed to Superconstructor are NOT applied. So it is not possible to obtain a REFRESH_TOKEN.Expected behavior
Additional, provider specific OAuth2 options can be passed through the Superconstructor and become effective.
Minimal reproduction of the problem with instructions
My Google OAuth2 strategy implementation:
Environment
The text was updated successfully, but these errors were encountered: