1
votes

Hi I am working on implementation of authentication and authorization for my client SPA application and .Net core back end API application. I have registred two application in azure ad for SPA and API app. I am able to get the token using Postman as below.

postman to get token

I am able to get the token with required details. Now I am trying same thing in front end react app. I am using react adal library.

I have below code to get token

   /* istanbul ignore file */
import { adalGetToken, AuthenticationContext, UserInfo } from 'react-adal';
import { UserProfile } from '../common/models/userProfile';
class AdalContext {
    private authContext: AuthenticationContext | any;
    private appId: string = '';
    private endpoints: any;

    public initializeAdal(adalConfig: any) {
        debugger;
        this.authContext = new AuthenticationContext(adalConfig);
        this.appId = adalConfig.clientId;
        this.endpoints = adalConfig.endpoints.api;
    }
    get AuthContext() {
        return this.authContext;
    }

    public getToken(): Promise<string | void | null> {
        debugger;
        return adalGetToken(this.authContext, this.appId, this.endpoints).catch((error: any) => {
            if (error.msg === 'login_required' || error.msg === 'interaction_required') {
                this.authContext.login();
            }
        });
    }

    public acquireToken(callback: Function) {
        let user = this.AuthContext.getCachedUser();
        if (user) {
            this.authContext.config.extraQueryParameter = 'login_hint=' + (user.profile.upn || user.userName);
        }

        adalGetToken(this.authContext, this.appId, this.endpoints).then(
            (token) => {
                callback(token);
            },
            (error) => {
                if (error) {
                    if (error.msg === 'login_required') this.authContext.login();
                    else {
                        console.log(error);
                        alert(error);
                    }
                }
            }
        );
    }

    public getCachedToken(): string {
        return this.authContext.getCachedToken(this.authContext.config.clientId);
    }
    public getCachedUser(): UserInfo {
        return this.authContext.getCachedUser();
    }

    public getUserProfileData(): UserProfile {
        const user = this.authContext.getCachedUser();
        const userProfileData = new UserProfile();
        if (user) {
            userProfileData.name = user.profile.name;
            userProfileData.firstName = user.profile.given_name;
            userProfileData.surname = user.profile.family_name;
            userProfileData.emailAddress = user.userName;
            userProfileData.userProfileName = user.profile.name || user.userName;
        }
        return userProfileData;
    }

    public logOut() {
        this.authContext.logOut();
    }
}

const adalContext: AdalContext = new AdalContext();
export default adalContext;
export const getToken = () => adalContext.getToken();

I am able to generate token but problem here is in Aud field I am getting client id for SPA but my intention is to get Client Id of back end because I am generating token for my back end app. So I am struggling to set scope in react adal code. In Postman I am entering scope but here not sure how to pass cope. Does endpoints means scopes here? Also in postman I am passing client secrete of SPA app but here I do not see any option to pass client secrete. Can someone help me what wrong I am doing here or am I understood things in wrong way. any help would be appreciated. Thank you

1
I've posted my idea on your case and by the way, the api was maintained by yourself, even you provide the access token with the Aud of SPA client id, you can also consider it as the right Aud in the validation code. In other words, how to check whether the token is valid is up to yourself.tiny-wa
Actually I am expecting aud value as Back end client id because later with this help of token I want to implent on behalf of flow for my third app. In on behalf of flow Aud value in token and client id of my back end app should be sameMr Perfect
Is there any progress sir? I'm glad to get your response.tiny-wa

1 Answers

2
votes

You has a backend api program, so if you need to generate an access token for the api in it, you need to expose that api in azure ad then give api permission to an azure ad app(can be the one exposed api), then you can generate that token.

And I found a sample of using adal with react. And added the acquireToken code like below, as you can see that the scope is graph because here I wanna call the graph api. The same as yours, if you wanna generate the token used to call your own api, you need to change the parameter of acquireToken and change the url in ajax call.

And micrsoft has upgrade to recommend to use msal to replace adal, this sample is helpful.

authContext.acquireToken('https://graph.microsoft.com', function (error, token) {
        console.log("the token is:" + token);
        $.ajax({
          url: 'https://graph.microsoft.com/v1.0/me',
          headers:{'authorization':'Bearer '+ token},
          type:'GET',
          dataType:'json'
        }).done(function(res) {
          console.log(res);
        });
      })

==================================UPDATE===========================

Yes, I'll provide more details and pls forgive me if I misunderstood.

I mentioned 'expose an api' before. For example, you've registered an azure ad app call 'backendAPP', you can go to that app and expose an api follow the tutorial above, next, also this app, go to the api permission panel and click add permission->choose my apis->choose 'backendAPP'->pick up the permission and click add permissions at the bottom. Now you've finished the configuration.

enter image description here enter image description here enter image description here

If it's the first time you exposed an api, you may see this, the api is what you need to use in the code.

enter image description here

=============================UPDATE 2=======================================

I found the adalgetToken in react-adal.js is like below, I mean that there's only 2 parameters but in your code it seems 3 parameters.

/**
 * Return user token
 * @param authContext Authentication context
 * @param resource Resource GUID ot URI identifying the target resource.
 */
export function adalGetToken(authContext: AuthenticationContext, resourceUrl: string): Promise<string | null>;

You've defined 'this.authContext', I think you can use this.authContext.acquireToken('resource', callback) directly in your code.

my adalConfig.js, here I used the spa appid as the 'clientId', and certainly I've added the api permission of 'api://xx/accses_user'. I do this setting just for differing from the client app and the server app.

import { AuthenticationContext, adalFetch, withAdalLogin,adalGetToken } from 'react-adal';

export const adalConfig = {
tenant: 'e4c-----57fb',
clientId: '2c0e---f157',
endpoints: {
    api: 'api://33a01---aebfe202/user_access' // <-- The Azure AD-protected API
},
cacheLocation: 'localStorage',
};

export const authContext = new AuthenticationContext(adalConfig);

export const adalApiFetch = (fetch, url, options) =>
    adalFetch(authContext, adalConfig.endpoints.api, fetch, url, options);

export const withAdalLoginApi = withAdalLogin(authContext, adalConfig.endpoints.api);

export const adalGetToken2 = () =>
    adalGetToken(authContext, adalConfig.endpoints.api);

my app.js

import React, { Component } from 'react';
import { authContext, adalConfig,adalGetToken2} from './adalConfig';
import { runWithAdal } from 'react-adal';

const DO_NOT_LOGIN = false;

class App extends Component {

  state = {
    username: ''
  };

  componentDidMount() {

    runWithAdal(authContext, () => {
      // TODO : continue your process
      var user = authContext.getCachedUser();
      if (user) {        
        console.log(user);
        console.log(user.userName);
        this.setState({ username: user.userName });        
      }
      else {
          // Initiate login
          // authContext.login();        
        console.log('getCachedUser() error');
      }
  
      var token = authContext.getCachedToken(adalConfig.clientId)
      if (token) {        
        console.log(token);
      }
      else {        
        console.log('getCachedToken() error');       
      }

      authContext.acquireToken('api://33a01fed-253f-43e5-a0bb-0fffaebfe202/', function (error, token) {
        console.log("the token is:" + token);
      })

      // adalGetToken2().then(
      //   (token) => {
      //     console.log("the token2 is : "+token);
      //   },
      //   (error) => {
      //       if (error) {
      //           if (error.msg === 'login_required') this.authContext.login();
      //           else {
      //               console.log("the error is : "+error);
      //           }
      //       }
      //   }
      // );
     }, DO_NOT_LOGIN);
  }

  render() {
    return (
      <div>
        <p>username:</p>
        <pre>{ this.state.username }</pre>
      </div>
    );
  }
}

export default App;