如何将Bluemix APP-ID认证从Node.js传递至Angular5?
Hey Chris, let's break this down step by step to get your IBM App ID + Node.js/Express + Angular 5 setup working exactly how you want it. I've worked through similar flows before, so here's the practical, tested approach:
First, you need to make sure your Node.js backend not only handles authentication via App ID but also passes the necessary tokens back to your Angular frontend.
Install required dependencies:
Grab the packages you'll need for Passport and App ID integration:npm install passport ibmcloud-appid express --saveSet up the App ID Passport strategy:
In your Express app, configure the strategy using credentials from your Bluemix App ID dashboard:const passport = require('passport'); const AppIDStrategy = require('ibmcloud-appid').AppIDStrategy; passport.use(new AppIDStrategy({ clientId: 'YOUR_APP_ID_CLIENT_ID', tenantId: 'YOUR_APP_ID_TENANT_ID', oauthServerUrl: 'YOUR_APP_ID_OAUTH_SERVER_URL', redirectUri: 'http://localhost:4200/auth/callback' // Match your Angular callback route })); // Initialize passport middleware app.use(passport.initialize());Create login and callback routes:
The callback route is where App ID redirects post-authentication—here, extract tokens and send them to Angular:// Initiate App ID login flow app.get('/auth/login', passport.authenticate(AppIDStrategy.STRATEGY_NAME)); // Handle App ID callback app.get('/auth/callback', passport.authenticate(AppIDStrategy.STRATEGY_NAME, { failureRedirect: '/login' }), (req, res) => { // Extract tokens from the request object const { accessToken, idToken } = req.appIdTokens; // Redirect to Angular with tokens as query params res.redirect(`http://localhost:4200/dashboard?access_token=${accessToken}&id_token=${idToken}`); });
Once your backend redirects back to Angular, capture the tokens and store them securely for future API calls.
Create a callback component:
Generate a component to handle the/auth/callbackroute and extract tokens:import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; @Component({ selector: 'app-auth-callback', template: '<p>Processing login...</p>' }) export class AuthCallbackComponent implements OnInit { constructor(private route: ActivatedRoute, private router: Router) {} ngOnInit() { // Pull tokens from URL query parameters const accessToken = this.route.snapshot.queryParamMap.get('access_token'); const idToken = this.route.snapshot.queryParamMap.get('id_token'); if (accessToken && idToken) { // Store tokens (use sessionStorage for temporary, localStorage for persistent) sessionStorage.setItem('appid_access_token', accessToken); sessionStorage.setItem('appid_id_token', idToken); // Redirect to your main protected route this.router.navigate(['/dashboard']); } else { // Handle missing tokens error this.router.navigate(['/login']); } } }Update route configuration:
Add the callback route to yourapp-routing.module.ts:import { AuthCallbackComponent } from './auth-callback/auth-callback.component'; const routes: Routes = [ // ... your other routes { path: 'auth/callback', component: AuthCallbackComponent }, { path: 'dashboard', component: DashboardComponent } // Your protected dashboard ];
You have two main options depending on whether the data needs to persist long-term or stay frontend-only:
Option 1: Local Frontend Storage (Non-Sensitive Data)
For non-sensitive attributes (like UI preferences), wrap storage logic in a reusable service:
Create a UserService:
import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class UserService { private readonly USER_ATTRS_KEY = 'user_custom_attributes'; getUserAttributes(): Record<string, any> { const storedAttrs = localStorage.getItem(this.USER_ATTRS_KEY); return storedAttrs ? JSON.parse(storedAttrs) : {}; } setUserAttribute(key: string, value: any): void { const currentAttrs = this.getUserAttributes(); currentAttrs[key] = value; localStorage.setItem(this.USER_ATTRS_KEY, JSON.stringify(currentAttrs)); } removeUserAttribute(key: string): void { const currentAttrs = this.getUserAttributes(); delete currentAttrs[key]; localStorage.setItem(this.USER_ATTRS_KEY, JSON.stringify(currentAttrs)); } }Use the service in components:
import { UserService } from './user.service'; export class DashboardComponent { constructor(private userService: UserService) {} saveThemePreference() { this.userService.setUserAttribute('theme', 'dark-mode'); const activeTheme = this.userService.getUserAttributes().theme; // Returns 'dark-mode' } }
Option 2: Persist to Backend (Sensitive Data)
For sensitive attributes (like account settings), store them in your backend database, using the App ID access token to authenticate requests:
Backend: Create an attribute storage endpoint:
// Import App ID token validation middleware const validateToken = require('ibmcloud-appid').TokenValidationMiddleware; // Endpoint to save user attributes app.post('/api/user/attributes', validateToken(), (req, res) => { // Get unique user ID from the validated token const userId = req.user.sub; const userAttributes = req.body; // Save to your database (example with MongoDB-style syntax) db.collection('users').updateOne( { userId: userId }, { $set: userAttributes }, { upsert: true }, (err) => { if (err) return res.status(500).send('Failed to save attributes'); res.status(200).send('Attributes saved successfully'); } ); });Frontend: Call the backend endpoint:
import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class UserService { constructor(private http: HttpClient) {} saveAttributesToBackend(attrs: Record<string, any>) { const accessToken = sessionStorage.getItem('appid_access_token'); const headers = new HttpHeaders({ 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/json' }); return this.http.post('/api/user/attributes', attrs, { headers }); } }
- Token Safety: Avoid storing access tokens in
localStoragefor sensitive apps—usesessionStorageinstead, or explore HttpOnly cookie setups if your flow allows. - Token Validation: Always validate access tokens on the backend (the
TokenValidationMiddlewarehandles this for App ID tokens). - Sensitive Data: Never store passwords, payment info, or other sensitive data in frontend storage—always persist these to your backend with proper authentication.
内容的提问来源于stack exchange,提问作者Chris A

