microsoftgraph / microsoft-graph-toolkit

Authentication Providers and UI components for Microsoft Graph 🦒
https://docs.microsoft.com/graph/toolkit/overview
Other
932 stars 294 forks source link

mgt-login - Doesn't work when I use App Service level authentication #1313

Closed deepakkumar1984 closed 6 months ago

deepakkumar1984 commented 2 years ago

This is with reference to issue: https://github.com/microsoftgraph/microsoft-graph-toolkit/issues/572

I am using the mgt-login with the following config mgt.Providers.globalProvider = new mgt.Msal2Provider({ clientId: "xxxxxxxxxxxxxxxxxx", authority: "https://login.microsoftonline.com/xxxxxxxxx", scopes: ['People.Read', 'People.Read.All', 'User.Read', 'User.Read.All', 'Group.Read.All'], redirectUri: "https://xxxxxxxx.azurewebsites.net/" });

The Azure App is configured with the built-in App Service Authentication and configured with Azure Active Directory. Now what I understand if the auth by AAD doesn't mean the Msal2Provider login will work automatically even though both are configured with same App Registration Client ID.

Is there any documentation on how we can make both work together without asking user to login again to Msal2Provider when they are already authenticated using AAD>

Environment (please complete the following information):

ghost commented 2 years ago

Hello deepakkumar1984, thank you for opening an issue with us!

I have automatically added a "needs triage" label to help get things started. Our team will analyze and investigate the issue, and escalate it to the relevant team if possible. Other community members may also look into the issue and provide feedback 🙌

beth-panx commented 2 years ago

Hi @deepakkumar1984 - can you give it a try with loginHint as part of your provider configuration?

deepakkumar1984 commented 2 years ago

@beth-panx Yes I did that, and it still asks me to login again. The problem is since I have already loggedin via Azure AD auth to the web application, again have to relogin via mgt-login to make the people picker work which is not a great user experience. The loginHint unfortunatly didn't work.

beth-panx commented 2 years ago

Hi @deepakkumar1984 - we currently don't have a sample to do this yet and may need some investigation. Do you have an app or sample setup somewhere that I can work with? The general idea is that since the app service level auth already returns auto/access token, mgt should just take that token to retrieve the data instead of working with msal again.

ghost commented 2 years ago

This issue has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for 4 days. It will be closed if no further activity occurs within 3 days of this comment.

deepakkumar1984 commented 2 years ago

Ok sure will send an example to reproduce the issue.

joaquimpvh commented 2 years ago

Providers.globalProvider = new Msal2Provider({ clientId: 'YOUR_CLIENT_ID', scopes:['.default'], redirectUri: '/', authority: 'YOUR_AUTHORITY', });

function useIsSignedIn() { const [isSignedIn, setIsSignedIn] = useState(false); useEffect(() => { const updateState = () => { const provider = Providers.globalProvider; setIsSignedIn(provider && provider.state === ProviderState.SignedIn); if (provider.state === ProviderState.SignedOut) { console.log('Login redirect.'); provider.login(); } if (provider.state === 2) { setIsSignedIn(true); //console.log('Login feito !'); } else { //console.log('Login recusado !'); setIsSignedIn(false); } }; Providers.onProviderUpdated(updateState); updateState(); Providers.onActiveAccountChanged((ll) => { console.log('joaquim'); }); return () => { Providers.removeProviderUpdatedListener(updateState); } }, [isSignedIn]); return [isSignedIn]; }

export default function App() { const [isSignedIn] = useIsSignedIn();

//.....your code case isSignedIn is true

}

nmetulev commented 2 years ago

Thanks all - I'm going to open up this issue for anyone that might be interested in helping us build a sample for authenticating in an Azure App Service

carlo-quinonez commented 2 years ago

If we're talking about a Provider for an EasyAuth protected site, I wrote one. You'll have to update the AppRegistration to return Access Tokens for the Graph API as well.

import ky from 'ky';
import {
  Providers,
  SimpleProvider,
  ProviderState,
  createFromProvider,
} from '@microsoft/mgt-element';

/** Contains a manually entered auth token if provided, otherwise null */
const manualAuthToken =
window.location.hostname === 'localhost'
  ? window.prompt(
      "It seems like you're running locally. Please enter an access token to use the MS Graph API.\n\nYou can retrieve this token by\n  1. Visit the graph explorer https://developer.microsoft.com/en-us/graph/graph-explorer\n  2. Log in with your SSO credentials\n  3. Copy the value of the `Access token` tab.\n  4. Paste the access token below.",
    )
    : null;

/**
  * An EasyAuth Provider for @microsoft/mgt-components
  */
const provider = new SimpleProvider(async () => {
  return (
    manualAuthToken ??
    (await ky('/.auth/me').json())
      .filter((element) => element.access_token)
      .pop().access_token
  );
});
provider.graph = createFromProvider(provider);

// This is an authenticated request since we're behind easy auth.
provider.setState(ProviderState.SignedIn);

Providers.globalProvider = provider;

This can definitely be improved, we fetch an access token every request, but it was enough to get me started.

puthurr commented 2 years ago

Version with the token refresh (JS)

// MGT Authentication
var scopes = ["user.read"];

function getAccessToken(scopes) {
    return new Promise((resolve, reject) => {
        $.ajax("/.auth/refresh").done(function () {
            $.ajax({
                type: 'GET',
                url: '/.auth/me',
                dataType: 'json',
                success: function (data) {
                    token = data[0]["access_token"]; 
                    resolve(token);
                },
                error: function (error) {
                    reject(error);
                }
            });
        }).fail(function () {
            reject("Token failed to refresh...")
        });    
    });
}

function login() {
    //login code
    mgt.Providers.globalProvider.setState(mgt.ProviderState.SignedIn)
}

function logout() {
    // logout code
    mgt.Providers.globalProvider.setState(mgt.ProviderState.SignedOut)
}

let provider = new mgt.SimpleProvider(getAccessToken, login, logout);

mgt.Providers.globalProvider = provider;

You can call the login programmatically by invoking

mgt.Providers.globalProvider.login();

sebastienlevert commented 6 months ago

At the moment, this is not something we're likely to prioritize and the community provided a great sample on how to make it work! That being said, the Graph Toolkit is an open source project and we'll be happy to support and review if you want to contribute to its codebase! In the meantime, we will be closing this issue. Thanks!