【问题标题】:Angular get name from Auth0 observableAngular 从 Auth0 observable 获取名称
【发布时间】:2020-03-09 19:00:23
【问题描述】:

我想在登录后从 Auth0 userProfile$ 中提取名称。所以我可以用它在我们的数据库中创建一个顾问。我对可观察和订阅不太熟悉。我尝试在您可以在下面看到的代码中使用它。当我登录 subscribe 时,我可以看到我想添加到我的 post req 中的名称。但是当我尝试使用它时,它只会给我一个空订阅。

dashboard.component.ts
```typescript
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { ApiService } from '../services/api.service';
import { AuthService } from 'src/app/auth/auth.service';

@Component({
  selector: 'app-dashboard',
  templateUrl: './dashboard.component.html',
  styleUrls: ['./dashboard.component.scss'],
})
export class DashboardComponent implements OnInit {
  id= '';


  navigateSkillplatform() {
    this.router.navigate(['skillplatform', this.id]);
  }

  constructor(private router: Router, private api: ApiService, private auth: AuthService) {}

  ngOnInit(): void {


    var consultant = { Name: this.auth.userProfile$.subscribe(consultant => console.log("test", consultant.name)) };
    console.log(consultant.Name)
    // if (this.auth.loggedIn) {
    //   this.api.createConsultant(consultant).subscribe(
    //     response => {
    //       console.log("POST create consultant response:," , 
    //       response);
    // })
    // }
  }
}
```

Browser console output
<pre>
dashboard.component.ts:24 test Imke Van Rompa
dashboard.component.ts:25 
Subscriber {closed: false, _parentOrParents: null, _subscriptions: Array(1), syncErrorValue: null, syncErrorThrown: false, …}
closed: false
_parentOrParents: null
_subscriptions: [SubjectSubscription]
syncErrorValue: null
syncErrorThrown: false
syncErrorThrowable: true
isStopped: false
destination: SafeSubscriber {closed: false, _parentOrParents: null, _subscriptions: null, syncErrorValue: null, syncErrorThrown: false, …}
__proto__: Subscription
</pre>

Auth.service
```
    import { Injectable } from '@angular/core';
    import createAuth0Client from '@auth0/auth0-spa-js';
    import Auth0Client from '@auth0/auth0-spa-js/dist/typings/Auth0Client';
    import { authConfig } from '../../environments/environment';
    import { from, of, Observable, BehaviorSubject, combineLatest, throwError } from 'rxjs';
    import { tap, catchError, concatMap, shareReplay } from 'rxjs/operators';
    import { Router } from '@angular/router';

    @Injectable({
      providedIn: 'root',
    })
    export class AuthService {
      // Create an observable of Auth0 instance of client
      auth0Client$ = (from(
        createAuth0Client({
          domain: authConfig.domain,
          client_id: authConfig.clientId,
          redirect_uri: `${window.location.origin}`,
          audience: authConfig.audience,
        })
      ) as Observable<Auth0Client>).pipe(
        shareReplay(1), // Every subscription receives the same shared value
        catchError(err => throwError(err))
      );

      // Manage Acces Token
      // Observable method to retrieve access token and make it available for use in application
      getTokenSilently$(options?): Observable<string> {
        return this.auth0Client$.pipe(
          concatMap((client: Auth0Client) => from(client.getTokenSilently(options)))
        );
      }

      // Define observables for SDK methods that return promises by default
      // For each Auth0 SDK method, first ensure the client instance is ready
      // concatMap: Using the client instance, call SDK method; SDK returns a promise
      // from: Convert that resulting promise into an observable
      isAuthenticated$ = this.auth0Client$.pipe(
        concatMap((client: Auth0Client) => from(client.isAuthenticated())),
        tap(res => (this.loggedIn = res))
      );
      handleRedirectCallback$ = this.auth0Client$.pipe(
        concatMap((client: Auth0Client) => from(client.handleRedirectCallback()))
      );
      // Create subject and public observable of user profile data
      private userProfileSubject$ = new BehaviorSubject<any>(null);
      userProfile$ = this.userProfileSubject$.asObservable();
      // Create a local property for login status
      loggedIn: boolean = null;

      constructor(private router: Router) {
        // On initial load, check authentication state with authorization server
        // Set up local auth streams if user is already authenticated
        this.localAuthSetup();
        // Handle redirect from Auth0 login
        this.handleAuthCallback();
      }

      // When calling, options can be passed if desired
      // https://auth0.github.io/auth0-spa-js/classes/auth0client.html#getuser
      getUser$(options?): Observable<any> {
        return this.auth0Client$.pipe(
          concatMap((client: Auth0Client) => from(client.getUser(options))),
          tap(user => this.userProfileSubject$.next(user))
        );
      }

      private localAuthSetup() {
        // This should only be called on app initialization
        // Set up local authentication streams
        const checkAuth$ = this.isAuthenticated$.pipe(
          concatMap((loggedIn: boolean) => {
            if (loggedIn) {
              // If authenticated, get user and set in app
              // NOTE: you could pass options here if needed
              return this.getUser$();
            }
            // If not authenticated, return stream that emits 'false'
            return of(loggedIn);
          })
        );
        checkAuth$.subscribe();
      }

      login(redirectPath: string = '/') {
        // A desired redirect path can be passed to login method
        // (e.g., from a route guard)
        // Ensure Auth0 client instance exists
        this.auth0Client$.subscribe((client: Auth0Client) => {
          // Call method to log in
          client.loginWithRedirect({
            redirect_uri: `${window.location.origin}`,
            appState: { target: redirectPath },
          });
        });
      }

      private handleAuthCallback() {
        // Call when app reloads after user logs in with Auth0
        const params = window.location.search;
        if (params.includes('code=') && params.includes('state=')) {
          let targetRoute: string; // Path to redirect to after login processsed
          const authComplete$ = this.handleRedirectCallback$.pipe(
            // Have client, now call method to handle auth callback redirect
            tap(cbRes => {
              // Get and set target redirect route from callback results
              targetRoute = cbRes.appState && cbRes.appState.target ? cbRes.appState.target : '/';
            }),
            concatMap(() => {
              // Redirect callback complete; get user and login status
              return combineLatest([this.getUser$(), this.isAuthenticated$]);
            })
          );
          // Subscribe to authentication completion observable
          // Response will be an array of user and login status
          authComplete$.subscribe(([user, loggedIn]) => {
            // Redirect to target route after callback processing
            this.router.navigate([targetRoute]);
          });
        }
      }

      logout() {
        // Ensure Auth0 client instance exists
        this.auth0Client$.subscribe((client: Auth0Client) => {
          // Call method to log out
          client.logout({
            client_id: authConfig.clientId,
            returnTo: window.location.origin,
          });
        });
      }
    }
    ```

    I am sure there is going to be a simple answer for this that I just didn't think of yet.

【问题讨论】:

  • 请出示您的身份验证服务。

标签: angular observable auth0 subscribe


【解决方案1】:

我认为您应该只订阅并映射到订阅内的名称。

ngOnInit(): void {
    const consultant = {}
    this.auth.userProfile$.subscribe(result=> {
          console.log("test", result.name)
          consultant.name = result.name
    });
}

【讨论】:

    【解决方案2】:

    试试这样的:

    export class DashboardComponent implements OnInit {
      id= '';
      consultantName;
    
      navigateSkillplatform() {
        this.router.navigate(['skillplatform', this.id]);
      }
    
      constructor(private router: Router, private api: ApiService, private auth: AuthService) 
      {}
    
      ngOnInit(): void {
        this.auth.userProfile$.subscribe(consultant => this.consultantName = consultant.name);
    
        console.log(this.consultantName)
        // Now you have name for your consultant and you can make post to your db
        if (this.auth.loggedIn && this.consultantName) {
           this.api.createConsultant(this.consultantName).subscribe(
             response => {
               console.log("POST create consultant response:," , 
               response);
          })
        }
      }
    }
    

    【讨论】:

    • 这不会在所有情况下都有效,并且会导致内存泄漏,因为您永远不会关闭订阅。 subscribe() 函数是异步的,因此您不确定this.consultantName 是否会填充到subscribe() 之后的行中另外,如果顾问为空,您将在subscribe() 中收到错误Could not get name of null
    【解决方案3】:

    也许用途应该改变

    import { Component, OnInit } from '@angular/core';
    import { Router } from '@angular/router';
    import { ApiService } from '../services/api.service';
    import { AuthService } from 'src/app/auth/auth.service';
    
    @Component({
      selector: 'app-dashboard',
      templateUrl: './dashboard.component.html',
      styleUrls: ['./dashboard.component.scss'],
    })
    export class DashboardComponent implements OnInit {
      id= '';
    
    
      navigateSkillplatform() {
        this.router.navigate(['skillplatform', this.id]);
      }
    
      constructor(private router: Router, private api: ApiService, private auth: AuthService) {}
    
      ngOnInit(): void {
    
    
        var consultant = { Name: this.auth.userProfile$.subscribe(consultant => console.log("test", consultant.name)) };
        console.log(consultant.Name)
        // if (this.auth.loggedIn) {
        //   this.api.createConsultant(consultant).subscribe(
        //     response => {
        //       console.log("POST create consultant response:," , 
        //       response);
        // })
        // }
      }
    }
    

    import { Component, OnInit, OnDestroy } from '@angular/core';
    import { Router } from '@angular/router';
    import { ApiService } from '../services/api.service';
    import { AuthService } from 'src/app/auth/auth.service';
    import { takeUntil } from 'rxjs/operators';
    import { Subject } from 'rxjs';
    
    @Component({
      selector: 'app-dashboard',
      templateUrl: './dashboard.component.html',
      styleUrls: ['./dashboard.component.scss'],
    })
    export class DashboardComponent implements OnInit, OnDestroy {
      id= '';
      consultant = null;
      onDestroy = new Subject<void>();
    
      navigateSkillplatform() {
        this.router.navigate(['skillplatform', this.id]);
      }
    
      constructor(private router: Router, private api: ApiService, private auth: AuthService) {}
    
      ngOnInit(): void {
    
    
        this.auth.userProfile$.pipe(takeUntil(this.onDestroy)).subscribe(consultant => 
            this.consultant = consultant;
            // Do whaterever you want with the user.
            console.log(this.consultant)
        );
    
        }
      }
    
      ngOnDestroy() { this.onDestroy.emit(); }
    }
    

    【讨论】:

      猜你喜欢
      • 2020-06-11
      • 2016-07-02
      • 2019-05-21
      • 2020-12-19
      • 2019-10-15
      • 1970-01-01
      • 2017-12-16
      • 2017-07-25
      • 1970-01-01
      相关资源
      最近更新 更多