Auth interceptor can be plugged in to a HttpClient to ensure passing proper authorization data to all requests and automatically refresh the tokens when they expire. You can configure whether authorization is done by body, header or query param, you can define your own refresh mechanism or use some ready-made, commonly used options.
Quick Start
Add the Auth Interceptor package, assuming that you already have @coolio/http installed:
npminstall@coolio/auth-interceptor
Declare httpClient as described in @coolio/http's README.md and then use createAuthInterceptor, passing all parameters required by options object. authInterceptor itself may also make it's own calls (via reauthorize function), so you may need to create another HttpClient, which won't interfere with your basic HttpClient's interceptors. See the Usage section for complete example.
Usage
import { BodyCasing, bodyParser, bodySerializer, fetchRequestHandler, getHostname, HttpClient, NormalizedHttpOptions} from'@coolio/http';import { createAuthInterceptor } from'@coolio/auth-interceptor';/** * This client is only used for authorization purposes - it doesn't have any interceptors. */constauthorizationClient=newHttpClient({ requestHandler:fetchRequestHandler(), responseParser:bodyParser({ bodyCasing:BodyCasing.CAMEL_CASE }), bodySerializer:bodySerializer({ bodyCasing:BodyCasing.SNAKE_CASE }), baseUrl:'https://my-domain.org/'});constauthInterceptor=createAuthInterceptor({reauthorize:async () => {constresponse=awaitauthorizationClient.post('/oauth/token', { body: { clientId:'client_id', refreshToken:'refresh_token_from_store', grantType:'refresh_token', }, });const { accessToken,refreshToken,tokenType,expires } =awaitresponse.parsedBody();// Store received tokens somewhere (let's say... local storage?)// Note: it's not 100% safe to store tokens that waylocalStorage.setItem('secrets',JSON.stringify({ accessToken, refreshToken, tokenType, expires, })); },setAuthorizationData: (options:NormalizedHttpOptions) => {// Get previously stored tokensconst { accessToken,tokenType } =JSON.parse(localStorage.getItem('secrets') ||'{}');if (!options.headers) {options.headers = {}; }options.headers['Authorization'] = tokenType ?`${tokenType}${accessToken}`: accessToken; },onAuthorizationFailure: (error:Error) => {console.log(error.message);// You may clear the app state here and redirect user to login pagethrow error; },canAuthorize: (options:NormalizedHttpOptions) =>getHostname(options.url) ==='my-domain.org',});exportconsthttpClient=newHttpClient({ requestHandler:fetchRequestHandler(), responseParser:bodyParser({ bodyCasing:BodyCasing.CAMEL_CASE }), bodySerializer:bodySerializer({ bodyCasing:BodyCasing.SNAKE_CASE }), baseUrl:'https://my-domain.org/'}).addInterceptor(authInterceptor);
Now each request directed to my-domain.org has a proper Authorization header. In case of 401 response, all requests are paused and token refresh happens. When token is fresh, all requests are resumed. The whole process is transparent and doesn't affect calls.
OAuth2 Usage
If you're using OAuth2 authentication mechanisms, you can use an interceptor pre-made specifically for that case. See an example below:
import { BodyCasing, bodyParser, bodySerializer, ContentType, fetchRequestHandler, getHostname, HttpClient, NormalizedHttpOptions,} from'@coolio/http';import { createOAuth2Interceptor } from'@coolio/auth-interceptor';constauthInterceptor=createOAuth2Interceptor({ httpClientOptions: {// You can override default client options used for making refresh token requests }, clientId:'client-id', clientSecret:'client-secret', contentType:ContentType.JSON,// URL-encoded by defaultgetAuthorizationData: () =>JSON.parse(localStorage.getItem('auth')),setAuthorizationData: (data) =>localStorage.setItem('auth',JSON.stringify(data)),canAuthorize: (options:NormalizedHttpOptions) =>getHostname(options.url) ==='my-domain.org', refreshTokenUrl:'https://my-domain.org/auth/token',onAuthorizationFailure: (error:Error) => {console.log(error.message);// You may clear the app state here and redirect user to login pagethrow error; },});exportconsthttpClient=newHttpClient({ requestHandler:fetchRequestHandler(), responseParser:bodyParser({ bodyCasing:BodyCasing.CAMEL_CASE }), bodySerializer:bodySerializer({ bodyCasing:BodyCasing.SNAKE_CASE }), baseUrl:'https://my-domain.org/'}).addInterceptor(authInterceptor);