Storing Client ID for Google OAuth: Best Practices

I used the following link to enable my users to authenticate using Google:
https://dev.to/jorgecf/integrating-google-authentication-with-your-angular-app-4j2a

However, I encountered a situation where I had to include my Google Client ID in the UI code:

return pload.then(async () => {
  await gapi.auth2
    .init({ client_id: 'abc123' })  // Inserting my client ID here. How should I securely store this?
    .then(auth => {
      this.gapiSetup = true;
      this.authInstance = auth;
    });
});

Could having the client ID in the UI code pose a security risk? If so, what would be the best way to store and handle it?

Answer №1

To the Point

Storing it anywhere within your website is possible, typically done using JavaScript.

In-Depth Explanation

I have encountered similar inquiries before, so I believe it is a valid question.

The Oauth2 flow requires the client_id in the browser, which does not pose a security risk.

Why? Because it is outlined that way in the OAUTH2 SPECIFICATION.

Why is the client_id necessary?

Your Angular code needs the client_id to form the auth url and subsequently redirect to that URL. Regardless of the oauth2 provider (such as Google or Facebook), the URL follows this syntax:

https://auth.acme.com/auth?response_type=code&redirect_uri=https://myweb.com/callback&client_id=******

Note the redirect_uri and client_id fields.

This auth url redirects users to the login form (Google, Facebook, Microsoft, etc.), then collects their credentials and redirects back to your site with the code value at the redirect_uri.

This process is known as Oauth2 Authorization Code Flow/Grant. More information can be found here: https://auth0.com/docs/get-started/authentication-and-authorization-flow/authorization-code-flow

What could be problematic?

Holding the secret on your frontend (Angular) raises security concerns.

The client_secret and client_id, combined with other details, are essential for obtaining the access_token from providers like Google. This token grants access to APIs (Drive, Maps, etc.) if proper permissions are granted.

This exchange should take place on your backend, not in the frontend!

Additional instructions on acquiring the access_token using client_id, client_secret, and auth_code can be found here:

How to Partially Conceal It?

Create the auth url on your backend and return it to your frontend with a 302 http code for immediate redirection by the browser.

It is referred to as partial concealment because a curious individual could halt the redirection (using the escape key) or examine your auth url containing the client_id through the browser console.

Where Should the client_id Be Stored?

If you recognize that the visibility of the client_id is not an issue, feel free to store it anywhere in your frontend:

  • Global JavaScript variable (prior to SPA transpilation)
  • HTML templating (Server-Side Rendering)

Ultimately, you will require the complete auth url (once per user session), not just the client_id. Therefore, instead of constructing the auth url in the frontend, generate it in your backend and retrieve it in the frontend. Example:

GET /v1/authorize/url

Where to Keep the access_token?

A scenario akin to storing the client_id. The access_token can be stored anywhere online since it's consistently needed to consume web services, REST APIs, or microservices. Common storage locations include:

The same principles apply to mobile platforms:

  • Android SharedPreferences: Android save user session

Do not hesitate to join the discussion room for additional OAuth2-related queries.

Answer №2

My preferred method is to embed Open Id Connect settings in a configuration file alongside your web static content.

Take a look at this sample configuration file I created for reference. Remember, access tokens cannot be obtained just from this information - valid credentials are still required.

In many cases, components like UIs and APIs go through various stages before deployment:

  • DEV
  • QA
  • STAGING
  • PRODUCTION

Your continuous delivery process can handle the transmission of different settings to each stage of the pipeline. This way, your code remains clean and free of any hardcoded values.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Eliminate all citation markers in the final compiled result

Currently, I am consolidating all my .ts files into a single file using the following command: tsc -out app.js app.ts --removeComments This is based on the instructions provided in the npm documentation. However, even after compilation, all reference tag ...

Updating the model in Angular 2 using two-way binding

I am looking to make some updates to my Angular 2 model. Here is my update method: putDep(item) { var headers = new Headers(); headers.append('Content-Type', 'application/json'); this.selected.departmentName = item.depart ...

Creating a variable within an ngFor loop

For my angular2 project, I need to display a matrix of workers and courses. Here's the current code I am using: <tr *ngFor="let worker of workers"> <td class="{{worker.fired ? 'fired-worker':''}}">{{worker.lastName ...

Issue encountered with passport-local in TypeScript: Unable to utilize 'new' with an expression that does not have a call or construct signature

Currently, I am attempting to implement the passport-local package in TypeScript (version 2.0.0RC); however, a compiler error has arisen: Error TS2351: It is not possible to use 'new' with an expression lacking a call or construct signature. ...

Ways to circumvent ng switch and create a component based on type

In my current code, I have an array called resourceTypes and I am using ngSwitch to create different components/directives based on the TypeName. However, I find this approach cumbersome as I have to update the code every time I add a new resource editor. ...

What is the process for calculating a class property in typescript?

If I were writing this in JavaScript, it would look like: function a(b,c) {this.foo = b; this.bar = c; this.yep = b+c} // undefined b = new a(1,2) // a {foo: 1, bar: 2, yep: 3} However, I've been struggling to achieve the same in TypeScript. None of ...

What is the most effective method for data binding using [innerHTML] in Angular 4?

One issue I've encountered is that in Angular 4, there are limited ways to perform data binding between HTML and TypeScript, such as {{myText}}, [], (), and [innerHTML]="myText". Which method is the most effective for binding a simple variable to HTM ...

Utilize the failure of Travis due to issues with a Git submodule to improve

I am facing an issue with my Angular project that has a git submodule for the backend. When I build the project on my local machine, it is successful. However, when I try to build it on Travis, it fails. npm ERR! enoent ENOENT: no such file or directory, ...

The Wave Tool detected an Accessibility issue with a mat-select element that utilizes mat-label, specifically flagging an Orphaned form label warning

Utilizing mat-select, a dropdown is implemented alongside a label for the dropdown via mat-label. Upon conducting an accessibility check with the Wave Evaluation Tool chrome extension, an Orphaned form label Warning was detected (indicating that the label ...

Issue with Angular 6: Unable to locate identifier 'require' while trying to request xml2json node

Recently delving into Angular, I've been exploring the installed node modules and how to use them in a data.service.ts file. Everything below executes flawlessly! var isWindows = require('is-windows'); console.log("Is this windows? " + isWi ...

The method JSON.stringify is not properly converting the entire object to a string

JSON.stringify(this.workout) is not properly stringifying the entire object. The workout variable is an instance of the Workout class, defined as follows: export class Workout { id: string; name: string; exercises: Exercise[]; routine: Ro ...

Resetting an Angular reactive form while excluding a specific field

After referencing the discussion on https://stackoverflow.com/questions/50197347/how-to-reset-only-specific-fields-of-form-in-angular-5, I am facing a challenge with resetting my form. My form consists of 20 fields and when the user clicks the reset opti ...

Refreshing an Angular Progressive Web App on the home screen of an Android device both as an app and in

I have developed an Angular PWA that automatically checks for new versions and updates the app code. It works flawlessly on desktop browsers, but I am facing an issue on mobile devices when accessing the application via a home screen shortcut created with ...

When an input is populated, the onchange event does not trigger in a React application

When setting the input with a ref like this: refName.current.value = 'my value' The value will be set in the DOM, but the onChange event does not trigger. Is there a way to make the onchange event fire when using a ref? import React, { useRe ...

Issue with Redirect URI in Angular application causing authentication problems for Strapi and Microsoft integration

After carefully following the instructions outlined in this guide, I have configured all settings related to the App Registration, Strapi Provider, and MSAL. However, when the authentication process initiates, I am directed to a Microsoft login screen wher ...

Angular - Implementing validation for maximum character length in ngx-intl-tel-input

Utilizing the ngx-intl-tel-input package in my Angular-12 project multistep has been quite promising. Below is a snippet of the code: Component: import { SearchCountryField, CountryISO, PhoneNumberFormat } from 'ngx-intl-tel-input'; expor ...

Issues surrounding the presentation of error validation messages

I need assistance with validating a registration form. The form includes fields for email, password, and confirm-password. To validate the email, I use a pattern for correctness, while for the password I require one uppercase letter, one lowercase letter, ...

Steer clear of nesting subscribe functions when a forkjoin is present within

Below is my angular code: this.service.save(body).pipe( switchMap(resp => { return this.dialog.confirmation({ message: 'save object successfully!' }); }), filter(ok => ok), tap(() => { this.pro.status = re ...

Attempting to update a variable within a function results in an error message stating: "Attempting to use block-scoped variable '...' before it has been declared.ts(2448)"

I'm currently working on a typescript function that takes in a numeric array (defined as type: number[]) and computes the mean. I want to make sure that the function can handle cases where the input array includes some null values. To address this, I ...

Cannot execute example of type alias in Typescript

While delving into the Typescript documentation, I came across the concept of type alias and found an interesting example here. type DescribableFunction = { description: string; (someArg: number): boolean; }; function doSomething(fn: DescribableFunctio ...