DEV Community

Hamza Ahmed
Hamza Ahmed

Posted on • Updated on

#Angular Adventure: Mastering Secure Local Storage in Angular with Crypto-js

Hey there, fellow Angular enthusiasts! I'm super excited to share my very first post with you all, and I hope you'll enjoy it as much as I enjoyed writing it. Today, we're going to talk about local storage, an essential aspect of web applications, and how to use it in Angular like a pro. We'll also delve a little into some advanced topics like using crypto-js to encrypt and decrypt data. So, let's dive right in, shall we? 😄

Part 1: What is Local Storage, and why should we care?

Local storage is like a little treasure chest for your web apps, giving them a place to store all kinds of data client-side. Unlike its cousin, session storage, which disappears when you close your browser tab, local storage hangs around for the long haul (or at least until you clear it). It's super handy for storing things like JSON web tokens, user preferences, and app settings. However, it's important to remember that local storage can only store up to 5MB of data per domain.

Now, let's talk about how to work with local storage in Angular. First, I'll show you some plain ol' JavaScript methods to store, retrieve, remove, and clear data. Then, we'll sprinkle some Angular magic on top to create a service wrapper for our local storage API. Ready to learn? Let's do this!

Part 2: Working with Local Storage in Angular

Alright, now that we've got a basic understanding of local storage, let's see how to use it in Angular. First, we'll create a new Angular application using the Angular CLI:

ng new local-app

Enter fullscreen mode Exit fullscreen mode

Once your Angular app is up and running, create a new Angular service with this command:

ng g service local

Enter fullscreen mode Exit fullscreen mode

Our service is going to be a simple wrapper for the local storage API, with methods to add, get, remove, and clear data. Here's what our LocalService looks like:

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class LocalService {

  constructor() { }

  public saveData(key: string, value: string) {
    localStorage.setItem(key, value);
  }

  public getData(key: string) {
    return localStorage.getItem(key)
  }
  public removeData(key: string) {
    localStorage.removeItem(key);
  }

  public clearData() {
    localStorage.clear();
  }
}
Enter fullscreen mode Exit fullscreen mode

To use our shiny new LocalService in our app, we'll inject it into a component. For this example, let's use the app.component.ts file. Import LocalService, and create an instance of it in the constructor method:

import { Component } from '@angular/core';
import { LocalService } from './local.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {

  constructor(private localStore: LocalService) {

  }
}
Enter fullscreen mode Exit fullscreen mode

Now that we've got our service set up, let's talk about something a little more advanced: encrypting and decrypting data with crypto-js.

Part 3: Encrypting and Decrypting Data with Crypto-js

Local storage is great and all, but it's not the most secure place to store sensitive data. To add an extra layer of protection, we can use a library called crypto-js to encrypt and decrypt our data before storing and fetching it from local storage.

First, let's install crypto-js in our Angular project using npm:

npm install crypto-js
npm i --save-dev @types/crypto-js
Enter fullscreen mode Exit fullscreen mode

With crypto-js installed, we can import it into our LocalService:

import * as CryptoJS from 'crypto-js';
Enter fullscreen mode Exit fullscreen mode

Now we'll add two new methods to our service to handle encryption and decryption:

private encrypt(txt: string): string {
  return CryptoJS.AES.encrypt(txt, this.key).toString();
}

private decrypt(txtToDecrypt: string) {
  return CryptoJS.AES.decrypt(txtToDecrypt, this.key).toString(CryptoJS.enc.Utf8);
}
Enter fullscreen mode Exit fullscreen mode

With these methods in place, we can update our saveData and getData methods to use encryption and decryption:

public saveData(key: string, value: string) {
  localStorage.setItem(key, this.encrypt(value));
}

public getData(key: string) {
  let data = localStorage.getItem(key) || "";
  return this.decrypt(data);
}
Enter fullscreen mode Exit fullscreen mode

And here's the complete local.service.ts file:

import { Injectable } from '@angular/core';
import * as CryptoJS from 'crypto-js';

@Injectable({
  providedIn: 'root'
})
export class LocalService {

  key = "123";

  constructor() { }

  public saveData(key: string, value: string) {
    localStorage.setItem(key, this.encrypt(value));
  }

  public getData(key: string) {
    let data = localStorage.getItem(key) || "";
    return this.decrypt(data);
  }
  public removeData(key: string) {
    localStorage.removeItem(key);
  }

  public clearData() {
    localStorage.clear();
  }

  private encrypt(txt: string): string {
    return CryptoJS.AES.encrypt(txt, this.key).toString();
  }

  private decrypt(txtToDecrypt: string) {
    return CryptoJS.AES.decrypt(txtToDecrypt, this.key).toString(CryptoJS.enc.Utf8);
  }
}
Enter fullscreen mode Exit fullscreen mode

To test our new encrypted local storage functionality, let's store some data in our app.component.ts file:

import { Component } from '@angular/core';
import { LocalService } from './local.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'local-app';

  constructor(private localStore: LocalService) {}

  ngOnInit(): void {
    this.localStore.saveData('id', 'jk123');
    console.log('decrypted data', this.localStore.getData('id'));
  }
}
Enter fullscreen mode Exit fullscreen mode

After saving and running the Angular app, you'll see that the data is now encrypted in local storage. Type localStorage in the browser console, and you'll see something like this:

{
  "id": "U2FsdGVkX1+xovPDV0m0um6MPlrb+p5aG8Rb09TSWaM="
}
Enter fullscreen mode Exit fullscreen mode

Pretty cool, huh? The data is encrypted, and only our app can decrypt it. When accessing local storage data within the application, you'll get the decrypted data, as shown in the console.log call in the ngOnInit method above.

Remember to always think about security when using local storage, especially when handling sensitive data. Local storage is a powerful tool, but with great power comes great responsibility. Happy coding!

Top comments (7)

Collapse
 
maxart2501 profile image
Massimo Artizzu

Ok, but how do you keep the key secure?

Collapse
 
toddhd profile image
Todd Davis

You can keep the key secure by putting it into your environment variables in Angular and then pull it from there.

Collapse
 
maxart2501 profile image
Massimo Artizzu

We're talking about runtime security. Once in the browser, the key is clear.

Thread Thread
 
toddhd profile image
Todd Davis

This IS about runtime security. You store private keys in enviroment.ts, then they are hidden at runtime.

pazel.dev/how-to-keep-your-secrets...

Thread Thread
 
maxart2501 profile image
Massimo Artizzu

They are not.

They might be stuffed inside the compiled bundle, but they'd still available in plain text inside of it. The environment file is part of the code served to the client.

It usually wouldn't matter for API keys, because they're checked against the web app host, but if we're talking about a cypher key that's not the solution to keep it secret. Not even with code obfuscation.

Collapse
 
trireme profile image
trireme

Thats a good question, there is no advantage to encrypt the data without secure storage of the key.

Collapse
 
samuk10 profile image
Samuel Sampaio

well thanks for sharin it! will add to my project lab