-
Notifications
You must be signed in to change notification settings - Fork 34
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
Authentication Token #51
Comments
In case anyone is interested, this works well with ASP.NET Authentication (and with technically any Bearer token): import { Injectable } from '@angular/core';
import { GrpcEvent, GrpcMessage, GrpcRequest } from '@ngx-grpc/common';
import { GrpcHandler, GrpcInterceptor } from '@ngx-grpc/core';
import { Observable } from 'rxjs';
@Injectable()
export class GrpcAuthenticationInjector implements GrpcInterceptor
{
intercept<Q extends GrpcMessage, S extends GrpcMessage>(
request: GrpcRequest<Q, S>,
next: GrpcHandler
): Observable<GrpcEvent<S>>
{
const token = 12345; // take it from the store
request.requestMetadata.set('Authorization', `Bearer ${token}`);
return next.handle(request);
}
} |
Hi @isc30 answering multiple questions, sorry for delay :)
Here is the example implementation that I mentioned above (inspired by https://github.com/serhiisol/ngx-auth/blob/master/src/auth.interceptor.ts) import { Injectable, Injector } from '@angular/core';
import { GrpcEvent, GrpcMessage, GrpcRequest, GrpcStatusEvent } from '@ngx-grpc/common';
import { GrpcHandler, GrpcInterceptor } from '@ngx-grpc/core';
import { StatusCode } from 'grpc-web';
import { Observable, of, Subject, throwError } from 'rxjs';
import { first, map, switchMap } from 'rxjs/operators';
import { AuthError } from '../../../proto/.../auth.pb';
import { AuthService } from '../auth/auth.service';
import { Oauth2Service } from '../auth/oauth2.service';
@Injectable({
providedIn: 'root',
})
export class GrpcAuthInterceptor implements GrpcInterceptor {
private refreshInProgress = false;
private refreshSubject = new Subject<boolean>();
constructor(
private authService: AuthService,
private injector: Injector,
) {
}
intercept<Q extends GrpcMessage, S extends GrpcMessage>(request: GrpcRequest<Q, S>, next: GrpcHandler): Observable<GrpcEvent<S>> {
// skip for token operations
if (this.injector.get(Oauth2Service).isTokenResponse(request.responseClass)) {
return next.handle(request);
}
const doRequest = this.refreshInProgress ? this.waitUntilRefresh(request) : this.addToken(request);
return doRequest.pipe(
switchMap(req => next.handle(req)),
switchMap(event => {
if (event instanceof GrpcStatusEvent) {
if (event.statusCode === StatusCode.UNAUTHENTICATED) {
return this.loginRequired(event);
} else if (event.statusCode === StatusCode.PERMISSION_DENIED) {
switch (Number(event.metadata.get('reason'))) {
case AuthError.aeTokenExpired: return this.refresh(request, event);
case AuthError.aeMissingRequiredScope: return of(event); // pass through to the error handler
case AuthError.aeTokenInvalid: return this.loginRequired(event);
default: return this.loginRequired(event);
}
}
}
return of(event);
}),
);
}
private loginRequired(event: GrpcStatusEvent) {
this.authService.logout();
return throwError(event);
}
private refresh<Q extends GrpcMessage, S extends GrpcMessage>(request: GrpcRequest<Q, S>, event: GrpcStatusEvent) {
if (!this.refreshInProgress) {
this.refreshInProgress = true;
this.injector.get(Oauth2Service).refreshTokens().subscribe(
() => {
this.refreshInProgress = false;
this.refreshSubject.next(true);
},
() => {
this.refreshInProgress = false;
this.refreshSubject.next(false);
},
);
}
return this.retryAfterRefresh(request, event);
}
private addToken<Q extends GrpcMessage, S extends GrpcMessage>(request: GrpcRequest<Q, S>) {
return this.authService.getAccessToken().pipe(
first(),
map((token: string) => {
if (token) {
request.requestMetadata = request.requestMetadata.clone();
request.requestMetadata.set('Authorization', `Bearer ${token}`);
}
return request;
}),
);
}
private waitUntilRefresh<Q extends GrpcMessage, S extends GrpcMessage>(request: GrpcRequest<Q, S>) {
return this.refreshSubject.pipe(first(), switchMap(ok => ok ? this.addToken(request) : throwError(request)));
}
private retryAfterRefresh<Q extends GrpcMessage, S extends GrpcMessage>(request: GrpcRequest<Q, S>, event: GrpcEvent<S>) {
return this.refreshSubject.pipe(
first(),
switchMap(ok => ok ? this.injector.get<GrpcHandler>(GrpcHandler).handle(request) : of(event)),
);
}
} This interceptor after some adaptions could potentially be used with the rest of ngx-auth as well |
Hi, I'm trying to automatically send a token with every GRPC call after the client has logged in.
Is there any built-in way to do this? If not, would a custom Interceptor to set the metadata be enough for the job?
Maybe it's worth mentioning this usage in the docs?
Thanks
The text was updated successfully, but these errors were encountered: