1

I am trying to include a private API key in a service, by bringing it in from a private endpoint on the server. I am just not sure how to go about placing this into the service it's required in.

To be honest, I'm a little lost as to how to do this. This is my service below. The private API key is for a onesignal account which pushes mobile notifications to mobile devices.

EDITED QUESTION:

I've altered the code a little now and have made use of another service called KeysService. This then creates the response which allows me to define the this.authKey value - which is a string, as this is what I need.

As you will notice, the getKey() method is now being called in on the constructor and the console.log of this.authKeys is printing the relevant string I need to place into the Authorization Header found on the sendNotification() method below it.

import { Injectable } from '@angular/core';
import { OneSignal } from './onesignal';
import { environment } from '../environments/environments';
import { HttpClient, HttpHeaders, HttpBackend } from '@angular/common/http';

import { throwError } from 'rxjs';

import { KeysService } from './keys.service';

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

  authKey: string;

  api_url = environment.one_signal.api_url;

  private http: HttpClient;

  constructor(handler: HttpBackend, private keysService: KeysService) {
    this.http = new HttpClient(handler);
    this.getkey();
  }

  getkey() {
    this.keysService.getPrivateKeys()
      .subscribe(
        authKeys => {
          this.authKey = authKeys.data[0].key;
          console.log(this.authKey);
        }
      )
  }

  sendNotification(pushData: any) {
    const httpOptions = {
      headers: new HttpHeaders({
        "Content-Type": "application/json; charset=utf-8",
        "Authorization": "PRIVATE_API_KEY"
      })
    }
    return this.http.post<OneSignal>(this.api_url, pushData, httpOptions);
  }

  handleError(error) {
    let errorMessage = '';
    if (error.error) {
      errorMessage = error.error.message;
    } else {
      errorMessage = error;
    }
    return throwError(errorMessage);
  }
}

How do I bring in this.authKey into the sendNotification() method, say like (see below):

sendNotification(pushData: any) {
    const httpOptions = {
      headers: new HttpHeaders({
        "Content-Type": "application/json; charset=utf-8",
        "Authorization": this.authKey
      })
    }
    return this.http.post<OneSignal>(this.api_url, pushData, httpOptions);
  }

I think I am close, but I am missing a trick to get this to set.

2 Answers 2

1

You could try the following approach.

  1. When your receive private key from server (usually happens after user login), store it in browser's local storage or as a cookie.
  2. In your config service or similar, have a method which reads this private key from local storage or the cookie.
  3. Inject config service in required service to access the private key.

Also, instead of setting Authorization header for each call in every service, I suggest using an HttpInterceptor which sets this value for all http requests. Then you only need to inject the config service in that interceptor.

EDIT:

Storing the key in service itself:

As per your edited question, you are making a call to get the private key in the constructor itself. You can store the key just as any other variable in the service. And use it in the sendNotification() method just as you are doing now using this.authKey. What you have right now should work.

But if the page is reloaded, then all data in the service will be cleared. The service constructor will be called again making another call to get the private key.

Persisting the key on page reload:

If you want the persist the key on page reload, then you can store it in local storage, session storage or as a cookie.

Not storing the key anywhere:

If you don't want to store the key anywhere, and are fine with making a service call to get the private key every time sendNotification() method is called, then you can follow the below approach.

You can make two calls in succession — first call to get the private key, and then use the private key received as response from the first call as http header in the second call.

You can use flatMap from RxJS to do this.

sendNotification(pushData: any) {
   return this.keysService.getPrivateKeys()
      .flatMap(authKeys => {
         const httpOptions = {
           headers: new HttpHeaders({
             "Content-Type": "application/json; charset=utf-8",
             "Authorization": authKeys.data[0].key
          })
         };

         return this.http.post<OneSignal>(this.api_url, pushData, httpOptions);
    }
  }
Sign up to request clarification or add additional context in comments.

3 Comments

Hi Nikhil. Thanks for the input. After login, users have their token stored in local storage and an interceptor ensures that it is used for all requests, accept this service above, as the Authorization to OneSignal requires a different private key. You might have noticed I am using httpBackend, to skip the interceptor for this service, when posting the push notification. The issue I have is setting that private key for onesignal on the server and then calling it into the service using the locally stored user token as protection. If that makes sense, lol.
Thanks for the info @Apex. If I understand correctly, you want to make two calls, one for getting private key from your server, and another to OneSignal using the private key response received from first call. Check out my updated answer.
I have edited the question to show where I want to go with this. This may be a little clearer as to what I am trying to do.
0

I have got it working. I needed to pass the string value into the method inside the constructor. This way it can be found in other methods in the service:

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

import { OneSignal } from './onesignal';
import { environment } from '../environments/environments';
import { HttpClient, HttpHeaders, HttpBackend } from '@angular/common/http';

import { throwError } from 'rxjs';

import { KeysService } from './keys.service';

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

  authKey: string;
  api_url = environment.one_signal.api_url;

  private http: HttpClient;

  constructor(handler: HttpBackend, private keysService: KeysService) {
    this.http = new HttpClient(handler);
    this.getkey(this.authKey)
    .subscribe(
      authKeys => {
        this.authKey = authKeys.data[0].key;
      }
    )
  }

  getkey(authKey) {
    return this.keysService.getPrivateKeys();
  }

  sendNotification(pushData: any) {
    const httpOptions = {
      headers: new HttpHeaders({
        "Content-Type": "application/json; charset=utf-8",
        "Authorization": this.authKey
      })
    }
    return this.http.post<OneSignal>(this.api_url, pushData, httpOptions);
  }

  handleError(error) {
    let errorMessage = '';
    if (error.error) {
      errorMessage = error.error.message;
    } else {
      errorMessage = error;
    }
    return throwError(errorMessage);
  }
}

Now there is no placement of the PRIVATE_API_KEY in the front end code and the value is now accessed from a private endpoint accessible through a user token. The one signal service uses HttpBackend, so the request to OS won't get intercepted and will use the AUTHORIZATION settings in the service and set using this.authKey.

Tested it and it sends to all devices.

1 Comment

I edited my answer to your address your edited question. But looks like you figured it out. Glad you resolved it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.