Starts in:
01 DAYS
01 HRS
01 MIN
01 SEC
Contact Us

HTTP-Only Cookies in ASP.NET Zero Angular UI

How to enable HttpOnly cookies in ASP.NET Zero Angular

HttpOnly cookies are a type of cookie that can only be accessed and manipulated by the server through HTTP requests, not by JavaScript or client-side code. They enhance security by preventing cross-site scripting (XSS) attacks that could steal sensitive information stored in cookies.

First of all, your backend project and your Angular project must be located in the same domain. For example, if your Angular project is located in https://localhost:44300. Then your backend project *.Host must be located in https://localhost:44300/api.

Step 1: Configure your *Web.Core project

To implement HttpOnly cookies, we need to configure our token authentication controller. We will use the TokenAuthController.cs file in your *.Web.Core project to implement HttpOnly cookies.

Add IsHttpOnlyCookieEnabled Property

Add a new configuration to appsettings.json file in your *.Web.Host project to enable or disable HttpOnly cookies.

appsettings.json

{
  "Authentication": {
    "IsHttpOnlyCookieEnabled": true
  }
}

TokenAuthConfiguration class is used to read the configuration from appsettings.json file. Open TokenAuthConfiguration.cs file in your *.Web.Core project and add the following property to the class.

TokenAuthConfiguration.cs

public bool IsHttpOnlyCookieEnabled { get; set; }

Then, open *WebCoreModule class in your *.Web.Core project and add the following code to the ConfigureTokenAuth method.

// other codes...

tokenAuthConfig.IsHttpOnlyCookieEnabled = bool.Parse(_appConfiguration["Authentication:IsHttpOnlyCookieEnabled"] ?? "false");

Remove Token Properties from Models

To implement HttpOnly cookies effectively, our server application must refrain from returning access token and refresh token values directly from APIs. Consequently, we must remove these properties from our models and remove any unnecessary classes associated with them.

Remove following classes from your *.Web.Core project:

  • RefreshTokenResult.cs
  • ImpersonatedAuthenticateResultModel.cs
  • SwitchedAccountAuthenticateResultModel.cs

Adjust the method return types to Task and keep the generated access tokens, which will subsequently serve as HttpOnly cookies.

Then, remove accessToken and refreshToken properties from AuthenticateResultModel.cs and ExternalAuthenticateResultModel.cs files.

AuthenticateResultModel.cs

public class AuthenticateResultModel
{
    public bool ShouldResetPassword { get; set; }

    public string PasswordResetCode { get; set; }

    public long UserId { get; set; }

    public bool RequiresTwoFactorVerification { get; set; }

    public IList<string> TwoFactorAuthProviders { get; set; }

    public string TwoFactorRememberClientToken { get; set; }

    public string ReturnUrl { get; set; }
    
    public string c { get; set; }

    #region Used in Development

    public string RefreshToken { get; set; }

    public int RefreshTokenExpireInSeconds { get; set; }
    
    public string AccessToken { get; set; }

    public string EncryptedAccessToken { get; set; }
    
    public int ExpireInSeconds { get; set; }

    #endregion
}

ExternalAuthenticateResultModel.cs

public class ExternalAuthenticateResultModel
{
    public bool WaitingForActivation { get; set; }

    public string ReturnUrl { get; set; }

    #region Used in Development

    public string RefreshToken { get; set; }

    public int RefreshTokenExpireInSeconds { get; set; }
    
    public string AccessToken { get; set; }

    public string EncryptedAccessToken { get; set; }
    
    public int ExpireInSeconds { get; set; }

    #endregion
}

Configure TokenAuthController.cs

  • Create a new method to add access token and refresh token as HttpOnly cookies.
private void AddAccessTokenCookie(string accessToken)
{
    Response.Cookies.Append("Abp.AuthToken",
        accessToken,
        new CookieOptions
        {
            Expires = Clock.Now.Add(_configuration.AccessTokenExpiration),
            HttpOnly = true,
            Secure = true,
            SameSite = SameSiteMode.None
        }
    );
}

private void AddRefreshTokenCookie(string refreshToken)
{
    Response.Cookies.Append("Abp.AuthRefreshToken",
        refreshToken,
        new CookieOptions
        {
            Expires = Clock.Now.Add(_configuration.RefreshTokenExpiration),
            HttpOnly = true,
            Secure = true,
            SameSite = SameSiteMode.None
        }
    );
}
  • Go to the Authenticate method and change return statement with following code.
// other codes...

// For development
if (!_configuration.IsHttpOnlyCookieEnabled)
{
    return new AuthenticateResultModel
    {
        AccessToken = accessToken,
        ExpireInSeconds = (int) _configuration.AccessTokenExpiration.TotalSeconds,
        RefreshToken = refreshToken.token,
        RefreshTokenExpireInSeconds = (int) _configuration.RefreshTokenExpiration.TotalSeconds,
        EncryptedAccessToken = GetEncryptedAccessToken(accessToken),
        TwoFactorRememberClientToken = twoFactorRememberClientToken,
        UserId = loginResult.User.Id,
        ReturnUrl = returnUrl
    };
}

AddAccessTokenCookie(accessToken);
AddRefreshTokenCookie(refreshToken.token);

return new AuthenticateResultModel
{
    TwoFactorRememberClientToken = twoFactorRememberClientToken,
    UserId = loginResult.User.Id,
    ReturnUrl = returnUrl
};
  • Go to the RefreshToken method, change the return statement with following code.
if (!_configuration.IsHttpOnlyCookieEnabled)
{
    return await Task.FromResult(new RefreshTokenResult(
        accessToken,
        GetEncryptedAccessToken(accessToken),
        (int) _configuration.AccessTokenExpiration.TotalSeconds)
    );
}

AddAccessTokenCookie(accessToken);

return await Task.FromResult(new RefreshTokenResult(
    string.Empty,
    string.Empty, 
    (int) _configuration.AccessTokenExpiration.TotalSeconds)
);
  • Go to the ImpersonatedAuthenticate, DelegatedImpersonatedAuthenticate, LinkedAccountAuthenticate methods and update return statements like we did in RefreshToken method.

  • Go to the ExternalAuthenticate method and update the method as following code.

[HttpPost]
public async Task<ExternalAuthenticateResultModel> ExternalAuthenticate(
    [FromBody] ExternalAuthenticateModel model)
{
    var externalUser = await GetExternalUserInfo(model);

    var loginResult = await _logInManager.LoginAsync(
        new UserLoginInfo(model.AuthProvider, externalUser.ProviderKey, model.AuthProvider),
        GetTenancyNameOrNull()
    );

    switch (loginResult.Result)
    {
        case AbpLoginResultType.Success:
        {
            // One Concurrent Login 
            if (AllowOneConcurrentLoginPerUser())
            {
                await ResetSecurityStampForLoginResult(loginResult);
            }

            var refreshToken = CreateRefreshToken(
                await CreateJwtClaims(
                    loginResult.Identity,
                    loginResult.User,
                    tokenType: TokenType.RefreshToken
                )
            );

            var accessToken = CreateAccessToken(
                await CreateJwtClaims(
                    loginResult.Identity,
                    loginResult.User,
                    refreshTokenKey: refreshToken.key
                )
            );

            var returnUrl = model.ReturnUrl;

            if (model.SingleSignIn.HasValue && model.SingleSignIn.Value &&
                loginResult.Result == AbpLoginResultType.Success)
            {
                loginResult.User.SetSignInToken();
                returnUrl = AddSingleSignInParametersToReturnUrl(
                    model.ReturnUrl,
                    loginResult.User.SignInToken,
                    loginResult.User.Id,
                    loginResult.User.TenantId
                );
            }
            
            if (!_configuration.IsHttpOnlyCookieEnabled)
            {
                return new ExternalAuthenticateResultModel
                {
                    AccessToken = accessToken,
                    EncryptedAccessToken = GetEncryptedAccessToken(accessToken),
                    ExpireInSeconds = (int) _configuration.AccessTokenExpiration.TotalSeconds,
                    ReturnUrl = returnUrl,
                    RefreshToken = refreshToken.token,
                    RefreshTokenExpireInSeconds = (int) _configuration.RefreshTokenExpiration.TotalSeconds
                };
            }
            
            AddAccessTokenCookie(accessToken);
            AddRefreshTokenCookie(refreshToken.token);
            
            return new ExternalAuthenticateResultModel
            {
                ReturnUrl = returnUrl,
            };
        }
        case AbpLoginResultType.UnknownExternalLogin:
        {
            var newUser = await RegisterExternalUserAsync(externalUser);
            if (!newUser.IsActive)
            {
                return new ExternalAuthenticateResultModel
                {
                    WaitingForActivation = true
                };
            }

            //Try to login again with newly registered user!
            loginResult = await _logInManager.LoginAsync(
                new UserLoginInfo(model.AuthProvider, model.ProviderKey, model.AuthProvider),
                GetTenancyNameOrNull()
            );

            if (loginResult.Result != AbpLoginResultType.Success)
            {
                throw _abpLoginResultTypeHelper.CreateExceptionForFailedLoginAttempt(
                    loginResult.Result,
                    externalUser.EmailAddress,
                    GetTenancyNameOrNull()
                );
            }

            var refreshToken = CreateRefreshToken(await CreateJwtClaims(loginResult.Identity,
                loginResult.User, tokenType: TokenType.RefreshToken)
            );

            var accessToken = CreateAccessToken(await CreateJwtClaims(loginResult.Identity,
                loginResult.User, refreshTokenKey: refreshToken.key));
            
            if (!_configuration.IsHttpOnlyCookieEnabled)
            {
                return new ExternalAuthenticateResultModel
                {
                    AccessToken = accessToken,
                    EncryptedAccessToken = GetEncryptedAccessToken(accessToken),
                    ExpireInSeconds = (int) _configuration.AccessTokenExpiration.TotalSeconds,
                    RefreshToken = refreshToken.token,
                    RefreshTokenExpireInSeconds = (int) _configuration.RefreshTokenExpiration.TotalSeconds
                };
            }
            
            AddAccessTokenCookie(accessToken);
            AddRefreshTokenCookie(refreshToken.token);
            
            return new ExternalAuthenticateResultModel();
        }
        default:
        {
            throw _abpLoginResultTypeHelper.CreateExceptionForFailedLoginAttempt(
                loginResult.Result,
                externalUser.EmailAddress,
                GetTenancyNameOrNull()
            );
        }
    }
}

Create IsRefreshTokenAvailable Method

We must introduce to a new method to verify the availability of a refresh token. In your TokenAuthController.cs file, create a new method called IsRefreshTokenAvailable and insert the subsequent code:

[HttpGet]
public bool IsRefreshTokenAvailable()
{
    var refreshToken = Request.Cookies["Abp.AuthRefreshToken"];
    return !string.IsNullOrWhiteSpace(refreshToken);
}

Delete HttpOnly cookies in Logout method

To delete HttpOnly cookies, we need to configure the Logout method. In your TokenAuthController.cs file, find the Logout method and add the following code:

[HttpGet]
[AbpAuthorize]
public async Task LogOut()
{
    // Other codes ...
    
    Response.Cookies.Delete("Abp.AuthToken");
    Response.Cookies.Delete("Abp.AuthRefreshToken");
}

Create TenantIdCookieController.cs

Create a new controller for setting tenant id cookie. Create a new controller named TenantIdCookieController in your *.Web.Core project then add the following code:

using System;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace AngularHttpOnlyCookieDemo.Web.Controllers;

[Route("api/[controller]/[action]")]
public class TenantIdCookieController : AngularHttpOnlyCookieDemoControllerBase
{
    [HttpPost]
    public void SetTenantIdCookie(int tenantId)
    {
        if (tenantId == 0)
        {
            Response.Cookies.Delete("Abp.TenantId");
        }
        
        var cookieOptions = new CookieOptions
        {
            HttpOnly = true,
            Secure = true,
            Expires = DateTimeOffset.UtcNow.AddYears(1)
        };
        
        Response.Cookies.Append("Abp.TenantId", tenantId.ToString(), cookieOptions);
    }
}

Step 2: Configure your *.Web.Host project

To read HttpOnly cookies in incoming requests, we will create a middleware. This middleware will read the HttpOnly cookies in incoming requests and append them to the request headers. This way, we will be able to access the HttpOnly cookies in our requests.

Create HttpOnlyMiddleware.cs

Create a new middleware for adding HttpOnly cookies to the request headers. Create a new class named HttpOnlyMiddleware in your *.Web.Host project and add the following code:

using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;

namespace AngularHttpOnlyCookieDemo.Web.Startup;

public class HttpOnlyMiddleware
{
    private readonly RequestDelegate _next;
    
    public HttpOnlyMiddleware(RequestDelegate next)
    {
        _next = next;
    }
 
    public async Task Invoke(HttpContext context)
    {
        if (context.Request.Cookies.TryGetValue("Abp.AuthToken", out var token))
        {
            context.Request.Headers.Append("Authorization", "Bearer " + token);
        }

        await _next.Invoke(context);
    }
}

public static class HttpOnlyMiddlewareExtension
{
    public static IApplicationBuilder UseHttpOnlyToken(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<HttpOnlyMiddleware>();
    }
}

Configure Startup.cs

Utilize the crafted middleware in your application by adding app.UseHttpOnlyToken() to the Startup.cs file in your *.Web.Host project as illustrated below:

// Place before the Authentication middleware
app.UseHttpOnlyToken();

app.UseAuthentication();

Step 3: Configure your Angular project

Due to the changes made on the backend, we need to adjust and remove certain parts of the project in your Angular application.

Run npm run nswag command to update TokenAuthServiceProxy service.

Add IsHttpOnlyCookieEnabled property

Add a new property to abp.js file in src/assets/abp-web-resources folder to check if HttpOnly cookies are enabled.

abp.auth = abp.auth || {};

// Add this line
abp.auth.isHttpOnlyCookieEnabled = true;

Then add type definition for isHttpOnlyCookieEnabled property in typings.d.ts file.

declare namespace abp {
    // other codes...

    namespace auth{
        let isHttpOnlyCookieEnabled: boolean; 
    }
}

Update login.service.ts

Update login method at login.service.ts and remove accessToken and refreshToken parameters. Your code should look like this:

private login(
    accessToken: string,
    encryptedAccessToken: string,
    expireInSeconds: number,
    refreshToken: string,
    refreshTokenExpireInSeconds: number,
    rememberMe?: boolean,
    twoFactorRememberClientToken?: string,
    redirectUrl?: string
): void {

    if(!abp.auth.isHttpOnlyCookieEnabled){
        let tokenExpireDate = rememberMe ? new Date(new Date().getTime() + 1000 * expireInSeconds) : undefined;

        this._tokenService.setToken(accessToken, tokenExpireDate);

        if (refreshToken && rememberMe) {
            let refreshTokenExpireDate = rememberMe
                ? new Date(new Date().getTime() + 1000 * refreshTokenExpireInSeconds)
                : undefined;
            this._tokenService.setRefreshToken(refreshToken, refreshTokenExpireDate);
        }

        let self = this;
        this._localStorageService.setItem(
            AppConsts.authorization.encrptedAuthTokenName,
            {
                token: encryptedAccessToken,
                expireDate: tokenExpireDate,
            },
            () => {
                if (twoFactorRememberClientToken) {
                    self._localStorageService.setItem(
                        LoginService.twoFactorRememberClientTokenName,
                        {
                            token: twoFactorRememberClientToken,
                            expireDate: new Date(new Date().getTime() + 365 * 86400000), // 1 year
                        },
                        () => {
                            self.redirectToLoginResult(redirectUrl);
                        }
                    );
                } else {
                    self.redirectToLoginResult(redirectUrl);
                }
            }
        );
    }

    this.redirectToLoginResult(redirectUrl);
}

Then update processAuthenticateResult method at login.service.ts file as below:

private processAuthenticateResult(authenticateResult: AuthenticateResultModel, redirectUrl?: string) {
    this.authenticateResult = authenticateResult;

    if (authenticateResult.shouldResetPassword) {
        // Password reset

        this._router.navigate(['account/reset-password'], {
            queryParams: {
                c: authenticateResult.c,
            },
        });

        this.clear();
    } else if (authenticateResult.requiresTwoFactorVerification) {
        // Two factor authentication

        this._router.navigate(['account/send-code']);
    } else {
        // Successfully logged in

        if (authenticateResult.returnUrl && !redirectUrl) {
            redirectUrl = authenticateResult.returnUrl;
        }

        this.login(
            authenticateResult.accessToken,
            authenticateResult.encryptedAccessToken,
            authenticateResult.expireInSeconds,
            authenticateResult.refreshToken,
            authenticateResult.refreshTokenExpireInSeconds,
            this.rememberMe,
            authenticateResult.twoFactorRememberClientToken,
            redirectUrl
        );
    }
}

Update tryAuthWithRefreshToken method

Since cookies are http only, we can no longer access them via javascript, so we need to update this method.

Open src/app/account/auth/zero-refresh-token.service.ts and update as below.

import { Injectable } from '@angular/core';
import { RefreshTokenService, TokenService } from 'abp-ng2-module';
import { RefreshTokenResult, TokenAuthServiceProxy } from '@shared/service-proxies/service-proxies';
import { Observable, Subject, of } from 'rxjs';
import { AppConsts } from '@shared/AppConsts';
import { SignalRHelper } from '@shared/helpers/SignalRHelper';
import { LocalStorageService } from '@shared/utils/local-storage.service';

@Injectable({
    providedIn: 'root',
})
export class ZeroRefreshTokenService implements RefreshTokenService {
    constructor(
        private _tokenAuthService: TokenAuthServiceProxy,
        private _tokenService: TokenService,
        private _localStorageService: LocalStorageService
    ) {}

    tryAuthWithRefreshToken(): Observable<boolean> {
        if (abp.auth.isHttpOnlyCookieEnabled) {
            return this.tryAuthWithHttpOnlyRefreshToken();
        }

        return this.tryAuthWithLocalRefreshToken();
    }

    private tryAuthWithHttpOnlyRefreshToken(): Observable<boolean> {
        let refreshTokenObservable = new Subject<boolean>();

        this._tokenAuthService.isRefreshTokenAvailable().subscribe({
            next: (result) => {

                if (!result) {
                    refreshTokenObservable.next(false);
                    return;
                }

                this._tokenAuthService.refreshToken(undefined).subscribe({
                    next: () => refreshTokenObservable.next(true),
                    error: () => refreshTokenObservable.next(false)
                });
            }
        });

        return refreshTokenObservable;
    }

    private tryAuthWithLocalRefreshToken():  Observable<boolean>{
        let refreshTokenObservable = new Subject<boolean>();

        let token = this._tokenService.getRefreshToken();
        if (!token || token.trim() === '') {
            return of(false);
        }

        this._tokenAuthService.refreshToken(token).subscribe(
            (tokenResult: RefreshTokenResult) => {
                if (tokenResult && tokenResult.accessToken) {
                    let tokenExpireDate = new Date(new Date().getTime() + 1000 * tokenResult.expireInSeconds);
                    this._tokenService.setToken(tokenResult.accessToken, tokenExpireDate);

                    this._localStorageService.setItem(AppConsts.authorization.encrptedAuthTokenName, {
                        token: tokenResult.encryptedAccessToken,
                        expireDate: tokenExpireDate,
                    }, () => refreshTokenObservable.next(true));

                } else {
                    refreshTokenObservable.next(false);
                }
            },
            (error: any) => {
                refreshTokenObservable.next(false);
            }
        );
        return refreshTokenObservable;
    }
}

Update abp.auth.setToken usages

Open abp.js file in your src/assets/abp-web-resources folder.

Update the places where the abp.auth.setToken function is used

  • src/AppPreBootstrap.ts
  • src/account/auth/zero-refresh-token.service.ts
  • src/account/login/login.service.ts

Update the places where the abp.auth.getToken function is used

src/app/admin/users/users.component.ts

setUsersProfilePictureUrl(users: UserListDto[]): void {
    for (const user of users) {
        let profilePictureUrl: string;

        if (abp.auth.isHttpOnlyCookieEnabled) {
            profilePictureUrl = `${AppConsts.remoteServiceBaseUrl}/Profile/GetProfilePictureByUser?userId=${user.id}`;
        } else {
            this._localStorageService.getItem(AppConsts.authorization.encrptedAuthTokenName, (err, value) => {
                const token = encodeURIComponent(value);
                profilePictureUrl = `${AppConsts.remoteServiceBaseUrl}/Profile/GetProfilePictureByUser?userId=${user.id}&${AppConsts.authorization.encrptedAuthTokenName}=${token}`;
            });
        }

        (user as any).profilePictureUrl = profilePictureUrl;
    }
}

src/AppPreBootstrap.ts

private static getUserConfiguration(callback: () => void): any {
    if(!abp.auth.isHttpOnlyCookieEnabled){
        const token = abp.auth.getToken();

        let requestHeaders = AppPreBootstrap.getRequetHeadersWithDefaultValues();

        if (token) {
            requestHeaders['Authorization'] = 'Bearer ' + token;
        }
    }

    return XmlHttpRequestHelper.ajax(
        'GET',
        AppConsts.remoteServiceBaseUrl + '/AbpUserConfiguration/GetAll',
        null,
        null,
        (response) => {
            let result = response.result;

            _merge(abp, result);

            abp.clock.provider = this.getCurrentClockProvider(result.clock.provider);

            AppPreBootstrap.configureLuxon();

            abp.event.trigger('abp.dynamicScriptsInitialized');

            AppConsts.recaptchaSiteKey = abp.setting.get('Recaptcha.SiteKey');
            AppConsts.subscriptionExpireNootifyDayCount = parseInt(
                abp.setting.get('App.TenantManagement.SubscriptionExpireNotifyDayCount')
            );

            DynamicResourcesHelper.loadResources(callback);
        }
    );
}

src/app/shared/common/auth/app-auth.service.ts

@Injectable()
export class AppAuthService {
    logout(reload?: boolean, returnUrl?: string): void {

        let customHeaders = null;

        if(!abp.auth.isHttpOnlyCookieEnabled){
            customHeaders = {
                [abp.multiTenancy.tenantIdCookieName]: abp.multiTenancy.getTenantIdCookie(),
                Authorization: 'Bearer ' + abp.auth.getToken(),
            };
        }

        XmlHttpRequestHelper.ajax(
            'GET',
            AppConsts.remoteServiceBaseUrl + '/api/TokenAuth/LogOut',
            customHeaders,
            null,
            () => {
                this.logoutInternal(reload, returnUrl);
            },
            (errorResult: IAjaxResponse) => {
                if (errorResult.unAuthorizedRequest) {
                    abp.log.error(errorResult.error);
                    this.logoutInternal(reload, returnUrl);
                } else {
                    abp.message.error(errorResult.error.message);
                }
            }
        );
    }

    logoutInternal(reload?: boolean, returnUrl?: string): void {
        new LocalStorageService().removeItem(AppConsts.authorization.encrptedAuthTokenName, () => {
            if (reload !== false) {
                if (returnUrl) {
                    location.href = returnUrl;
                } else {
                    location.href = 'index.html';
                }
            }
        });
    }
}

Update following lines from file uploaders in:

  • src/app/admin/demo-ui-components/demo-ui-file-upload.component.ts
if (!abp.auth.isHttpOnlyCookieEnabled) {
    event.xhr.setRequestHeader('Authorization', 'Bearer ' + abp.auth.getToken());
}
  • src/app/shared/layout/profile/change-profile-picture-modal.component.ts
if(!abp.auth.isHttpOnlyCookieEnabled){
    this._uploaderOptions.authToken = 'Bearer ' + this._tokenService.getToken();
}
  • src/app/admin/settings/tenant-settings.component.ts
if (!abp.auth.isHttpOnlyCookieEnabled) {
    uploaderOptions.authToken = 'Bearer ' + this._tokenService.getToken();
}

Update setTenantIdCookie Method

Update setTenantIdCookie method in abp.js. Do api call to TenantIdCookieController instead of setting cookie directly. Your code should look like this:

abp.multiTenancy.setTenantIdCookie = function (tenantId) {
    return new Promise((resolve, reject) => {
        if (tenantId === undefined || tenantId === null) {
            tenantId = 0;
        }

        fetch(abp.appPath + 'api/TenantIdCookie/SetTenantIdCookie?tenantId=' + tenantId, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            }
        })
        .then(response => {
            if (!response.ok) {
                throw new Error('Network response was not ok');
            }
            return response.json();
        })
        .then(data => {
            console.log('Success:', data);
            resolve(data);
        })
        .catch(error => {
            console.error('Error:', error);
            reject(error);
        });
    });
    
};

As we are no longer able to handle cookies on the Angular side, switching tenants involves initiating an API call to the backend server. This API call facilitates the modification of cookies. Given our use of promise code in this API call, we have accordingly modified the setTenantIdCookie method to return a promise. Let's add definition of setTenantIdCookie method in typings.d.ts file.

/src/typings.d.ts

namespace multiTenancy{
    function setTenantIdCookie(tenantId?: number): Promise<void>;
}

Due to our utilization of Promises, find the usages of setTenantIdCookie in your project, and refactor them using the then and catch blocks as below:

src/AppPreBootstrap.ts

private static impersonatedAuthenticate(impersonationToken: string, tenantId: number, callback: () => void): void {
    abp.multiTenancy.setTenantIdCookie(tenantId).then(() => {

        let requestHeaders = AppPreBootstrap.getRequetHeadersWithDefaultValues();

        XmlHttpRequestHelper.ajax(
            'POST',
            AppConsts.remoteServiceBaseUrl +
            '/api/TokenAuth/ImpersonatedAuthenticate?impersonationToken=' +
            impersonationToken,
            requestHeaders,
            null,
            (response) => {
                let result = response.result;

                if(!abp.auth.isHttpOnlyCookieEnabled){
                    abp.auth.setToken(result.accessToken);
                }

                AppPreBootstrap.setEncryptedTokenCookie(result.encryptedAccessToken, () => {
                    callback();
                    location.search = '';
                });
            }
        );
    });
}

Conclusion

That's it! Now, you can use HttpOnly cookies in your ASP.NET Zero Angular application.

Angular http only cookie