manfredsteyer / angular-oauth2-oidc

Support for OAuth 2 and OpenId Connect (OIDC) in Angular.
MIT License
1.89k stars 687 forks source link

How can I set auto token refreshing in password flow? #897

Open woteska opened 4 years ago

woteska commented 4 years ago

How can I set auto token refreshing in password flow?

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    OAuthModule.forRoot({
      resourceServer: {
        allowedUrls: [`${environment.protocol}://${environment.hostname}`],
        sendAccessToken: true
      }
    })
  ],
  providers: [
    { provide: LocationStrategy, useClass: HashLocationStrategy },
    { provide: OAuthStorage, useValue: sessionStorage },
  ],
  bootstrap: [AppComponent]
})
export class AppModule {
}
  private readonly authConfig: AuthConfig = {
    issuer: 'issuer',
    oidc: false,
    clientId: 'clientId',
    scope: 'openid username role apps full-api offline_access'
  };

In service constructor:

    this.oAuthService.configure(this.authConfig);
    this.oAuthService.setupAutomaticSilentRefresh();

1., .loadDiscoveryDocument() 2., .fetchTokenUsingPasswordFlowAndLoadUserProfile()

Login successful, user logged in.

After a while, it tries to call:

Request URL: http://10.10.10.10:10/connect/authorize?response_type=token&client_id=clientId&state=ZEx4dGR2alltRC02V2J3NEowWTJoa1pNbXp4R2lNaHNBTEZOTlVRZlB5T0dC&redirect_uri=&scope=openid%20username%20role%20apps%20full-api%20offline_access&prompt=none
Request Method: GET
Status Code: 302 Found

And I got an error after that:

http://10.10.10.10:10/home/error?errorId=CfDJ8PFLaJy2spFGuE1ynrI...
Request Method: GET
Status Code: 404 Not Found
Remote Address: 10.10.10.10:10
Referrer Policy: no-referrer-when-downgrade

Why does it want to call /connect/authorize for token refreshing, why not call connect/token? What is wrong with my config?

Desktop

jeroenheijmans commented 4 years ago

Hmm, interesting. It seems to be using the hidden-iframe silent refresh mechanism used for interactive (Code/Implicit) flows instead of the refresh token one.

woteska commented 4 years ago

@jeroenheijmans , you are right, the iframe appears in DOM when it tries to refresh the token. It looks like a bug, right?

This is the full config, but I don't think so it would count:

    strictDiscoveryDocumentValidation: false, // because no valid issuer
    issuer: 'edge-idp', // no fix hostname
    requireHttps: environment.protocol === 'https',
    oidc: false,
    showDebugInformation: false,
    clientId: 'clientId',
    scope: 'openid username role apps full-api offline_access'

Btw, I found another strange issue. When I call .revokeTokenAndLogout(), it calls /revocation POST 2 times. The config is the same you can see above.

  logout(): void {
    this.oAuthService.revokeTokenAndLogout()
      .catch(_ => this.oAuthService.logOut())
      .finally(() => this.router.navigate(['/login']));
  }
jeroenheijmans commented 4 years ago

The relevant docs do suggest Password flow is not supported. Not sure what gives though.

woteska commented 4 years ago

@jeroenheijmans quoting from this repo's README.MD:

"

"

Can you suggest a method to refresh the token? I did not find sample project for password flow unfortunately.

jeroenheijmans commented 4 years ago

No, sorry, no idea. RPO flow is deprecated and I don't need to use or support it anywhere.

If I would need to use it (e.g. with a legacy server) I would probably not use a library but some form of handcrafted solution instead. RPO is so small that an entire library almost feels like overkill, usually.

Not really an answer to your questions, sorry. Possibly another community member can help out? Or you could dig through the code to see how things should work, and let us know what you found. Good luck!

AlexGoris-KasparSolutions commented 4 years ago

We're using a password flow based auth mechanism and handle token refresh like so:

@Injectable({
  providedIn: 'root',
})
export class AuthService {
    constructor(private _oAuthService: OAuthService) {
        this._oAuthService.configure(authConfig);
        this._oAuthService.loadDiscoveryDocument();
        this._oAuthService.events.subscribe((e) => {
            switch (e.type) {
                case 'token_received':
                    this._oAuthService.loadUserProfile();
                    break;
                case 'discovery_document_loaded':
                    if (this._oAuthService.hasValidAccessToken()) this._oAuthService.loadUserProfile();
                    break;
                case 'token_expires':
                    this._oAuthService.refreshToken();
                    break;
                default:
                    break;
            }
        }
    }
}

This also handles loading of the user profile after getting an access token, and loading the user profile if the user still has a valid access token (from storage), which is in this way IMHO handled more elegantly than the provided examples in the documentation.

The token_expires event case will ensure that the token is refreshed when needed.