javascript - Getting request with auth token via Angular Interceptor and Intercepting further calls - Stack Overflow

I'm trying to authenticate to an API first and then "attach" the token to each one of my

I'm trying to authenticate to an API first and then "attach" the token to each one of my requests, but the one including 'connect'. I have the following:

Interceptor

@Injectable()
export class TokenInterceptor implements HttpInterceptor {
  constructor(private authService: AuthService) {}

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (!request.url.includes('/connect/token?grant_type=client_credentials')) {
      this.authService.isTokenExist()
      .pipe(
        catchError((error) => {
          return error;
        })
      )
      .subscribe((token: any) => {
        if (token) {
          request = request.clone({
            setHeaders: {
              Authorization: `Bearer ${token}`,
            },
          });
        }
        return next.handle(request);
      });
    }
    return next.handle(request); // here I'm hitting the point without token and I think this is the problem
  }
}

Service

import { Injectable } from '@angular/core';
import {
  HttpClient,
  HttpHeaderResponse,
  HttpHeaders,
} from '@angular/common/http';
import { environment } from '../../../environment';
import { urlString } from '../../utils/helpers';
import { Observable, catchError, of } from 'rxjs';
import { map } from 'rxjs/operators';

@Injectable({
  providedIn: 'root',
})
export class AuthService {
  constructor(private http: HttpClient) {}

  getToken(): Observable<string | null> {
    const requestBody: Request = {
      client_id: environment.client_id,
      client_secret: environment.client_secret,
      grant_type: environment.grant_type,
      scope: environment.scope,
    };
    const headers = new HttpHeaders().set(
      'Content-Type',
      'application/x-www-form-urlencoded'
    );
    const options = { headers };
    return this.http
      .post<Response>(
        `${environment.api_url}/connect/token?grant_type=client_credentials`,
        urlString(requestBody),
        options
      )
      .pipe(
        catchError((error) => {
          throw error;
        }),
        map((response: Response) => {
          this.setToken(response.access_token);
          return response.access_token;
        })
      );
  }

  getFileMetadata(fileId: number): Observable<any> {
    return this.http.get(
      `${environment.api_url}/api/v1.4/items/${fileId}/_metadata`
    );
  }

  isTokenExist(): Observable<string | null> {
    const tokenTime = localStorage.getItem('token_time');
    if (tokenTime) {
      const currentTime = new Date().getTime();
      const tokenTimeInt = parseInt(tokenTime, 10);
      if (currentTime - tokenTimeInt > 86400000) {
        return this.getToken();
      }
    } else {
      this.getToken().subscribe({
        next: (token) => {
          if (token) {
            this.setToken(token);
          }
          return of(token);
        },
        error: (error) => {
          throw error;
        }
      });
    }
    return of(localStorage.getItem('token'));
  }

  setToken(token: string): void {
    localStorage.setItem('token_', token);
    localStorage.setItem('token_time', new Date().getTime().toString());
  }
}

interface Request {
  grant_type: string;
  client_secret: string;
  client_id: string;
  scope: string;
}

interface Response {
  access_token: string;
  token_type: string;
  expires_in: number;
}

The result is that on the first-page load, I get 401, as the interceptor is just passing the request. The service returns the token on refresh, and the request passes as expected. My question is, how can we hold the Interceptor until the token is available and then intercept all other calls?

I'm trying to authenticate to an API first and then "attach" the token to each one of my requests, but the one including 'connect'. I have the following:

Interceptor

@Injectable()
export class TokenInterceptor implements HttpInterceptor {
  constructor(private authService: AuthService) {}

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (!request.url.includes('/connect/token?grant_type=client_credentials')) {
      this.authService.isTokenExist()
      .pipe(
        catchError((error) => {
          return error;
        })
      )
      .subscribe((token: any) => {
        if (token) {
          request = request.clone({
            setHeaders: {
              Authorization: `Bearer ${token}`,
            },
          });
        }
        return next.handle(request);
      });
    }
    return next.handle(request); // here I'm hitting the point without token and I think this is the problem
  }
}

Service

import { Injectable } from '@angular/core';
import {
  HttpClient,
  HttpHeaderResponse,
  HttpHeaders,
} from '@angular/common/http';
import { environment } from '../../../environment';
import { urlString } from '../../utils/helpers';
import { Observable, catchError, of } from 'rxjs';
import { map } from 'rxjs/operators';

@Injectable({
  providedIn: 'root',
})
export class AuthService {
  constructor(private http: HttpClient) {}

  getToken(): Observable<string | null> {
    const requestBody: Request = {
      client_id: environment.client_id,
      client_secret: environment.client_secret,
      grant_type: environment.grant_type,
      scope: environment.scope,
    };
    const headers = new HttpHeaders().set(
      'Content-Type',
      'application/x-www-form-urlencoded'
    );
    const options = { headers };
    return this.http
      .post<Response>(
        `${environment.api_url}/connect/token?grant_type=client_credentials`,
        urlString(requestBody),
        options
      )
      .pipe(
        catchError((error) => {
          throw error;
        }),
        map((response: Response) => {
          this.setToken(response.access_token);
          return response.access_token;
        })
      );
  }

  getFileMetadata(fileId: number): Observable<any> {
    return this.http.get(
      `${environment.api_url}/api/v1.4/items/${fileId}/_metadata`
    );
  }

  isTokenExist(): Observable<string | null> {
    const tokenTime = localStorage.getItem('token_time');
    if (tokenTime) {
      const currentTime = new Date().getTime();
      const tokenTimeInt = parseInt(tokenTime, 10);
      if (currentTime - tokenTimeInt > 86400000) {
        return this.getToken();
      }
    } else {
      this.getToken().subscribe({
        next: (token) => {
          if (token) {
            this.setToken(token);
          }
          return of(token);
        },
        error: (error) => {
          throw error;
        }
      });
    }
    return of(localStorage.getItem('token'));
  }

  setToken(token: string): void {
    localStorage.setItem('token_', token);
    localStorage.setItem('token_time', new Date().getTime().toString());
  }
}

interface Request {
  grant_type: string;
  client_secret: string;
  client_id: string;
  scope: string;
}

interface Response {
  access_token: string;
  token_type: string;
  expires_in: number;
}

The result is that on the first-page load, I get 401, as the interceptor is just passing the request. The service returns the token on refresh, and the request passes as expected. My question is, how can we hold the Interceptor until the token is available and then intercept all other calls?

Share Improve this question asked Nov 18, 2024 at 11:05 VladynVladyn 5831 gold badge6 silver badges20 bronze badges 3
  • 1 Maybe you can use async / await? – Marco Commented Nov 20, 2024 at 5:01
  • 1 I've got a bit rusty with the latest Angular, but yes, you are right; it could be async imperative flavor. But I've read on the web that angular direction is more reactive. That's why I'm trying to follow it with RxJs. – Vladyn Commented Nov 20, 2024 at 8:33
  • I see, well I'm more experienced in Vue, but with vue-router you can conditionally user a middleware / interceptor to check if you need auth / token, maybe this is the way to go? – Marco Commented Nov 21, 2024 at 5:10
Add a comment  | 

2 Answers 2

Reset to default 1

One advice would be to take the token from local storage since your already set it there. Most probably you save it on login and should be available for every call you will do afterwards. You should not call an endpoint in interceptor. Interceptors are primarily designed to inspect and manipulate HTTP requests and responses globally, such as adding headers, logging, or handling errors.

Then in interceptor you can get your token and attach it to header

const token = localStorage.getItem(‘token’);
let headers = request.headers;
if (token) {
 headers = headers.set('Authorization', `Bearer ${token}`);
}
 const req = request.clone({ headers });

And then pass the new ‘req’ as your request.

I did this refactoring in the interceptor:

@Injectable()
export class TokenInterceptor implements HttpInterceptor {
  constructor(private authService: AuthService) {}

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (request.url.includes('/connect/token?grant_type=client_credentials')) {
      return next.handle(request);
    }
    return this.authService.isTokenExist().pipe(
      catchError((error) => {
        throw error;
      }),
      switchMap((token: string) => {
        const headers = request.headers.set('Authorization', `Bearer ${token}`);
        const newRequest = request.clone({ headers });
        return next.handle(newRequest);
      })
    );
  }
}

...and in service, I changed methods like this:

import { Injectable } from '@angular/core';
import {
  HttpClient,
  HttpHeaderResponse,
  HttpHeaders,
} from '@angular/common/http';
import { environment } from '../../../environment';
import { urlString } from '../../utils/helpers';
import { Observable, catchError, of } from 'rxjs';
import { map } from 'rxjs/operators';

@Injectable({
  providedIn: 'root',
})
export class AuthService {
  constructor(private http: HttpClient) {}

  getToken(): Observable<string> {
    const requestBody: Request = {
      client_id: environment.client_id,
      client_secret: environment.client_secret,
      grant_type: environment.grant_type,
      scope: environment.scope,
    };
    const headers = new HttpHeaders().set(
      'Content-Type',
      'application/x-www-form-urlencoded'
    );
    const options = { headers };
    return this.http
      .post<Response>(
        `${environment.api_url}/connect/token?grant_type=client_credentials`,
        urlString(requestBody),
        options
      )
      .pipe(
        catchError((error) => {
          throw error;
        }),
        map((response: Response) => {
          this.setToken(response.access_token);
          return response.access_token;
        })
      );
  }

  isTokenExist(): Observable<any> {
    const tokenTime = localStorage.getItem('token_time');
    const token = localStorage.getItem('token');
    if (tokenTime) {
      const currentTime = new Date().getTime();
      const tokenTimeInt = parseInt(tokenTime, 10);
      if (currentTime - tokenTimeInt > 86400000) {
        return this.getToken();
      }
    }

    if (!token) {
      return this.getToken();
    }

    return of(localStorage.getItem('token'));
  }

  setToken(token: string): void {
    localStorage.setItem('token', token);
    localStorage.setItem('token_time', new Date().getTime().toString());
  }

  getFileMetadata(fileId: number): Observable<any> {
    return this.http.get(
      `${environment.api_url}/api/v1.4/items/${fileId}/_metadata`
    );
  }

  getFileBlob(uniformName: string, path: string): Observable<Blob> {
    const currentPath = path.replace(/\\/g, '/');
    return this.http.get(
      `${environment.api_url}/content/v1.4/${currentPath}/${uniformName}/_blob`,
      { responseType: 'blob' }
    );
  }
}

interface Request {
  grant_type: string;
  client_secret: string;
  client_id: string;
  scope: string;
}

interface Response {
  access_token: string;
  token_type: string;
  expires_in: number;
}

Basically the refactoring is in the Interceptor intercept function:

...
if (request.url.includes('/connect/token?grant_type=client_credentials')) {
      return next.handle(request);
    }
    return this.authService.isTokenExist().pipe(
      catchError((error) => {
        throw error;
      }),
      switchMap((token: string) => {
        const headers = request.headers.set('Authorization', `Bearer ${token}`);
        const newRequest = request.clone({ headers });
        return next.handle(newRequest);
      })
    );
....

Where by using early return is the request isn't a one for token. There service further, I'm returning an Observable instead if a string or null and a bit more logic in the method `isTokeExist()'

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745623061a4636639.html

相关推荐

  • 论文检测,文章检测,降AI率的工具

    1:文字滚筒鸭文字滚筒鸭作为市面上少见的全免费平台,文字滚筒鸭支持文章 AI 率检测、头条文章原创度分析、作文质量评估等多元场景,甚至能一键对比原文与改写后的相似度,所有功能统统 0 收费!精准算法秒出检测结果,不用充值会员,也无需分享裂变

    1小时前
    20
  • 如何增加 Elasticsearch 中的主分片数量

    要增加现有索引的主分片数量,直接修改是不可能的。因此,如果你想增加主分片的数量,必须重新创建索引。通常有两种方法:_reindex API 和 _split API。在这两种方法中,_split API 通常比 _reindex API 更

    1小时前
    20
  • windows 配置 upx

    ​1、下载:2、解压:解压后​3、配置环境变量,右键我的电脑-——》属性——》高级属性:在Path中添加: D:ProgramFileupx-5.0.0-win644、验证cmd中输入:代码语言:txt复制upx --version

    1小时前
    20
  • html制作一个放烟花动画的网页代码

    以下是一个用HTML制作的烟花动画网页代码,结合了粒子效果和重力模拟:html<!DOCTYPE html><html><head><title>烟花秀<title>

    1小时前
    20
  • 打破常规!支付宝小程序地图功能开发实用技巧,拓展业务场景

    打破常规!支付宝小程序地图功能开发实用技巧,拓展业务场景嘿,各位开发者小伙伴们

    1小时前
    00
  • MySQL 8.4 配置SSL组复制(八个步骤)

    环境这里有三台MySQL主机,分别是192.168.3.71,72,73,主机名分别对应71.3_mgr1,72.3_mgr2,73.3_mgr3,操作系统均为Oracle Linux 8.10 X64,MySQL版本均为MySQL 8.4

    1小时前
    00
  • 用安信可Ai

    以下作品由安信可社区用户业余菜狗制作前言自从接触智能家居之后,笔者就变得很依赖智能家居(绝对不是懒!)比如卧室灯,就在进门的地方,进门开灯很方便,但是晚上睡觉关灯就很不方便。之前是买了一款Wi-Fi灯,是用手机APP操作,刚开始用的时候感觉

    1小时前
    00
  • 群体遗传三剑客第三篇:megacc和ggtree进化树分析

    大家好,我是邓飞。之前计划写群体结构三剑客的博文,写了两篇了:搞起来!群体遗传三剑客:PCA、Admixture、进化树群体遗传三剑客第一篇:分组和不分组的PCA分析,添加解释百分比群体遗传三剑客第二篇:Admixture群体结构分析今天介

    1小时前
    00
  • MySQL8使用物理文件恢复MyISAM表测试

    我们现场测试一个场景,drop一张MyISAM表后,单独对这表进行物理恢复首先我们看一下secure_file_priv文件目录的位置代码语言:javascript代码运行次数:0运行复制mysql> show global vari

    1小时前
    00
  • AI生态暗战升级,科技巨头铁幕下的终极博弈

    一场围绕AI与智能体的标准、协议及生态的暗战已然蓄势待发。在美剧《权力的游戏》中,不到终局,主角归属始终成谜。如今的AI行业,正上演着同样扣人心弦的戏码。这并非是传统意义上的军事或政治博弈,而是一场围绕AI与智能体的标准、协议及生态展开的暗

    1小时前
    00
  • 电脑分区后进不了系统怎么办,修复教程

    电脑分区后进不了系统&#xff0c;可能的原因有多种&#xff0c;包括分区操作不当导致系统文件丢失或损坏、BIOS设置错误、引导文件未正确配置等。针对这些问题&#xff0c;可以尝试以下解决方法&#xff1

    58分钟前
    00
  • PackML over OPC UA

    在当今数字化转型的浪潮中,制造业正面临着前所未有的挑战与机遇。如何实现设备之间的高效通信与集成,成为提升生产效率、降低成本的关键。OPC UA(OPC Unified Architecture)与PackML(Packaging Machi

    50分钟前
    00
  • 万字图解 Java 并发框架:ForkJoin、CountDownLatch、Semaphore、CyclicBarrier

    本文图多,内容硬核,建议收藏。在第一章节《1.6w 字图解 Java 并发:多线程挑战、线程状态和通信、死锁;AQS、ReentrantLock、Condition 使用和原理》,我们开启了 Java 高并发系列的学习,透彻理解 Java

    48分钟前
    00
  • 10个 DeepSeek 神级提示词,建议收藏!

    在当下人工智能飞速发展的时代,DeepSeek 作为一款功能强大的 AI 工具,能够帮助我们实现各种创意和需求。然而,要充分发挥它的潜力,掌握一些巧妙的提示词至关重要。今天,就为大家精心整理了 15 个 DeepSeek 神级提示词,涵盖多

    44分钟前
    00
  • 长读长测序揭示结直肠癌异常可变剪接图谱与新型治疗靶点

    徐州医科大学肿瘤研究所董东郑骏年教授团队在Genome Medicine杂志发表题为“Long-read sequencing reveals the landscape of aberrant alternative splicing

    28分钟前
    00
  • OWASP TOP10

    什么是OWASP?它的全称是 Open Web Application Security Project(开放式 Web 应用程序 安全 项目)TOP 10OWASP Top 10的意思就是10项最严重的Web 应用程序安全风险列表 ,它总

    25分钟前
    00
  • 开源在线考试系统

    看到调问已经开始扩展在线考试的场景,试了一下,发现在线考试的基本能力都已经支持了。主要是考试中的各种计分功能,包括对每道题的选项设置分值计算、考试时间限制等,和官方了解了一下,考试中的其他各项能力也在逐步完善,有需求可以随时

    18分钟前
    00
  • CUT&amp;amp;Tag 数据处理和分析教程(7)

    过滤某些项目可能需要对比对质量分数进行更严格的过滤。本文细讨论了bowtie如何分配质量分数,并举例说明。MAPQ(x) = -10 * log10log10(P(x is mapped wrongly)) = -10 * log10(p)

    10分钟前
    10
  • module &#x27;torch.

    踩坑Ascend, 安装 pytorch 2.5.1 和 pytorch_npu 2.5.1, import torch 报错.执行 python -c "import torch;import torch_npu;"时

    2分钟前
    00
  • 【Docker项目实战】使用Docker部署IT工具箱Team·IDE

    一、Team·IDE介绍1.1 Team·IDE简介Team IDE 是一款集成多种数据库(如 MySQL、Oracle、金仓、达梦、神通等)与分布式系统组件(如 Redis、Zookeeper、Kafka、Elasticsearch)管理

    1分钟前
    00

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信