Мобильное приложение Мауи, используйте токен от msal, чтобы получить одиночную палатку и sp.list

У меня есть мобильное приложение Maui, и я использую msal для аутентификации. это работает нормально, и для входа в систему отображается экран входа в систему. после входа в систему я загружу новую страницу для вызова sp.list.

например. https://company.sharepont.com/sites/[имя сайта] /Lists/getbytitle('nameoflist')/items.

Все больше дней я искал предложения о том, как использовать httpclient с автотокеном, и заметил, что работа идет.

я использую это репо. https://github.com/carlfranklin/MsalAuthInMaui

Теперь, как использовать токен в процессе входа в систему для получения информации с sp.site. с httpclient или лучше с графиком.

Этот код не работает.

      using MAUI.MSALClient;
using Microsoft.Extensions.Configuration;
using Microsoft.Graph;
using Microsoft.Identity.Client;
using System.Net.Http.Headers;
using System.Reflection;
using CommunityToolkit.Maui;


namespace ESR_Mobile_app.Views
{

    public partial class Rapport : ContentPage
    {
        public Rapport()
        {
            InitializeComponent();
            

            IAccount cachedUserAccount = Task.Run(async () => await PublicClientSingleton.Instance.MSALClientHelper.FetchSignedInUserFromCache()).Result;
            _ = Dispatcher.DispatchAsync(async () =>
            {
                if (cachedUserAccount == null)
                {
                    await Shell.Current.GoToAsync("start");

                }

            });


        }


        private static async Task<string>CallWebAPIWithToken(AuthenticationResult authResult)
            {
                try
                {
                    //get data from API
                    HttpClient client = new HttpClient();
                    // create the request
                    HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Get, "https://company.sharepoint.com/sites/Projekte/_api/Lists/getbytitle('some_list')/items");

                    // ** Add Authorization Header **
                    message.Headers.Add("Authorization", authResult.CreateAuthorizationHeader());

                    // send the request and return the response
                    HttpResponseMessage response = await client.SendAsync(message).ConfigureAwait(false);
                    string responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
                    //return responseString;


                Console.WriteLine(responseString);

                     }
                //catch (Exception ex)
                catch (HttpRequestException e)
                    {
                //return ex.ToString();
                Console.WriteLine("\nException Caught!");
                Console.WriteLine("Message :{0} ", e.Message);

                     }

            }
                  
            private  async Task ShowMessage(string title, string message)
            {
                _ = this.Dispatcher.Dispatch(async () =>
                {
                    await  DisplayAlert(title, message, "OK").ConfigureAwait(false);
                });
            }
    }



}

Помощник MsalClient

      // Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Microsoft.Identity.Client;
using Microsoft.Identity.Client.Extensions.Msal;
using Microsoft.IdentityModel.Abstractions;
using System.Diagnostics;

namespace MAUI.MSALClient
{
    /// <summary>
    /// Contains methods that initialize and use the MSAL SDK
    /// </summary>
    public class MSALClientHelper
    {
        /// <summary>
        /// As for the Tenant, you can use a name as obtained from the azure portal, e.g. kko365.onmicrosoft.com"
        /// </summary>
        public AzureADConfig AzureADConfig;

        /// <summary>
        /// Gets the authentication result (if available) from MSAL's various operations.
        /// </summary>
        /// <value>
        /// The authentication result.
        /// </value>
        public AuthenticationResult AuthResult { get; private set; }

        /// <summary>
        /// Gets a value indicating whether this instance of PublicClientApp was initialized with a broker .
        /// </summary>
        /// <value>
        ///   <c>true</c> if this instance is broker initialized; otherwise, <c>false</c>.
        /// </value>
        public bool IsBrokerInitialized { get; private set; }

        /// <summary>
        /// Gets the MSAL public client application instance.
        /// </summary>
        /// <value>
        /// The public client application.
        /// </value>
        public IPublicClientApplication PublicClientApplication { get; private set; }

        /// <summary>
        /// This will determine if the Interactive Authentication should be Embedded or System view
        /// </summary>
        public bool UseEmbedded { get; set; } = false;

        /// <summary>
        /// The PublicClientApplication builder used internally
        /// </summary>
        private PublicClientApplicationBuilder PublicClientApplicationBuilder;

        // Token Caching setup - Mac
        public static readonly string KeyChainServiceName = "Contoso.MyProduct";

        public static readonly string KeyChainAccountName = "MSALCache";

        // Token Caching setup - Linux
        public static readonly string LinuxKeyRingSchema = "com.contoso.msaltokencache";

        public static readonly string LinuxKeyRingCollection = MsalCacheHelper.LinuxKeyRingDefaultCollection;
        public static readonly string LinuxKeyRingLabel = "MSAL token cache for Contoso.";
        public static readonly KeyValuePair<string, string> LinuxKeyRingAttr1 = new KeyValuePair<string, string>("Version", "1");
        public static readonly KeyValuePair<string, string> LinuxKeyRingAttr2 = new KeyValuePair<string, string>("ProductGroup", "Contoso");

        private static string PCANotInitializedExceptionMessage = "The PublicClientApplication needs to be initialized before calling this method. Use InitializePublicClientAppAsync() or InitializePublicClientAppForWAMBrokerAsync() to initialize.";

        /// <summary>
        /// Initializes a new instance of the <see cref="MSALClientHelper"/> class.
        /// </summary>
        public MSALClientHelper(AzureADConfig azureADConfig)
        {
            AzureADConfig = azureADConfig;

            this.InitializePublicClientApplicationBuilder();
        }

        /// <summary>
        /// Initializes the MSAL's PublicClientApplication builder from config.
        /// </summary>
        /// <autogeneratedoc />
        private void InitializePublicClientApplicationBuilder()
        {
            this.PublicClientApplicationBuilder = PublicClientApplicationBuilder.Create(AzureADConfig.ClientId)
                .WithAuthority(string.Format(AzureADConfig.Authority, AzureADConfig.TenantId))
                .WithExperimentalFeatures() // this is for upcoming logger
                .WithLogging(new IdentityLogger(EventLogLevel.Warning), enablePiiLogging: false)    // This is the currently recommended way to log MSAL message. For more info refer to https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/logging. Set Identity Logging level to Warning which is a middle ground
                .WithClientCapabilities(new string[] { "cp1" })                                     // declare this client app capable of receiving CAE events- https://aka.ms/clientcae

                .WithIosKeychainSecurityGroup("com.microsoft.adalcache");
        }

        /// <summary>
        /// Initializes the public client application of MSAL.NET with the required information to correctly authenticate the user.
        /// </summary>
        /// <returns></returns>
        public async Task<IAccount> InitializePublicClientAppAsync()
        {
            // Initialize the MSAL library by building a public client application
            this.PublicClientApplication = this.PublicClientApplicationBuilder
                .WithRedirectUri(PlatformConfig.Instance.RedirectUri)   // redirect URI is set later in PlatformConfig when the platform has been decided
                .Build();

            await AttachTokenCache();
            return await FetchSignedInUserFromCache().ConfigureAwait(false);
        }

        /// <summary>
        /// Initializes the public client application of MSAL.NET with the required information to correctly authenticate the user using the WAM broker.
        /// </summary>
        /// <returns>An IAccount of an already signed-in user (if available)</returns>
        public async Task<IAccount> InitializePublicClientAppForWAMBrokerAsync()
        {
            // Initialize the MSAL library by building a public client application
            this.PublicClientApplication = this.PublicClientApplicationBuilder
                .WithRedirectUri(PlatformConfig.Instance.RedirectUri)   // redirect URI is set later in PlatformConfig when the platform is decided
                .WithBroker()
                .WithParentActivityOrWindow(() => PlatformConfig.Instance.ParentWindow)   // This is required when using the WAM broker and is set later in PlatformConfig when the platform has been decided
                .Build();

            this.IsBrokerInitialized = true;

            await AttachTokenCache();
            return await FetchSignedInUserFromCache().ConfigureAwait(false);
        }

        /// <summary>
        /// Attaches the token cache to the Public Client app.
        /// </summary>
        /// <returns>IAccount list of already signed-in users (if available)</returns>
        private async Task<IEnumerable<IAccount>> AttachTokenCache()
        {
            if (DeviceInfo.Current.Platform != DevicePlatform.WinUI)
            {
                return null;
            }

            // Cache configuration and hook-up to public application. Refer to https://github.com/AzureAD/microsoft-authentication-extensions-for-dotnet/wiki/Cross-platform-Token-Cache#configuring-the-token-cache
            var storageProperties = new StorageCreationPropertiesBuilder(AzureADConfig.CacheFileName, AzureADConfig.CacheDir)
                    .Build();

            var msalcachehelper = await MsalCacheHelper.CreateAsync(storageProperties);
            msalcachehelper.RegisterCache(PublicClientApplication.UserTokenCache);

            // If the cache file is being reused, we'd find some already-signed-in accounts
            return await PublicClientApplication.GetAccountsAsync().ConfigureAwait(false);
        }

        /// <summary>
        /// Signs in the user and obtains an Access token for a provided set of scopes
        /// </summary>
        /// <param name="scopes"></param>
        /// <returns> Access Token</returns>
        public async Task<string> SignInUserAndAcquireAccessToken(string[] scopes)
        {
            Exception<NullReferenceException>.ThrowOn(() => this.PublicClientApplication == null, PCANotInitializedExceptionMessage);

            var existingUser = await FetchSignedInUserFromCache().ConfigureAwait(false);

            try
            {
                // 1. Try to sign-in the previously signed-in account
                if (existingUser != null)
                {
                    this.AuthResult = await this.PublicClientApplication
                        .AcquireTokenSilent(scopes, existingUser)
                        .ExecuteAsync()
                        .ConfigureAwait(false);
                }
                else
                {
                    if (this.IsBrokerInitialized)
                    {
                        Console.WriteLine("No accounts found in the cache. Trying Window's default account.");

                        this.AuthResult = await this.PublicClientApplication
                            .AcquireTokenSilent(scopes, Microsoft.Identity.Client.PublicClientApplication.OperatingSystemAccount)
                            .ExecuteAsync()
                            .ConfigureAwait(false);
                    }
                    else
                    {
                        this.AuthResult = await SignInUserInteractivelyAsync(scopes);
                    }
                }
            }
            catch (MsalUiRequiredException ex)
            {
                // A MsalUiRequiredException happened on AcquireTokenSilentAsync. This indicates you need to call AcquireTokenInteractive to acquire a token interactively
                Debug.WriteLine($"MsalUiRequiredException: {ex.Message}");

                this.AuthResult = await this.PublicClientApplication
                    .AcquireTokenInteractive(scopes)
                    .WithLoginHint(existingUser?.Username ?? String.Empty)
                    .ExecuteAsync()
                    .ConfigureAwait(false);
            }
            catch (MsalException msalEx)
            {
                Debug.WriteLine($"Error Acquiring Token interactively:{Environment.NewLine}{msalEx}");
                throw msalEx;
            }

            return this.AuthResult.AccessToken;
        }

        /// <summary>
        /// Signs the in user and acquire access token for a provided set of scopes.
        /// </summary>
        /// <param name="scopes">The scopes.</param>
        /// <param name="extraclaims">The extra claims, usually from CAE. We basically handle CAE by sending the user back to Azure AD for
        /// additional processing and requesting a new access token for Graph</param>
        /// <returns></returns>
        public async Task<String> SignInUserAndAcquireAccessToken(string[] scopes, string extraclaims)
        {
            Exception<NullReferenceException>.ThrowOn(() => this.PublicClientApplication == null, PCANotInitializedExceptionMessage);

            try
            {
                // Send the user to Azure AD for re-authentication as a silent acquisition wont resolve any CAE scenarios like an extra claims request
                this.AuthResult = await this.PublicClientApplication.AcquireTokenInteractive(scopes)
                        .WithClaims(extraclaims)
                        .ExecuteAsync()
                        .ConfigureAwait(false);
            }
            catch (MsalException msalEx)
            {
                Debug.WriteLine($"Error Acquiring Token:{Environment.NewLine}{msalEx}");
            }

            return this.AuthResult.AccessToken;
        }

        /// <summary>
        /// Shows a pattern to sign-in a user interactively in applications that are input constrained and would need to fall-back on device code flow.
        /// </summary>
        /// <param name="scopes">The scopes.</param>
        /// <param name="existingAccount">The existing account.</param>
        /// <returns></returns>
        public async Task<AuthenticationResult> SignInUserInteractivelyAsync(string[] scopes, IAccount existingAccount = null)
        {
            Exception<NullReferenceException>.ThrowOn(() => this.PublicClientApplication == null, PCANotInitializedExceptionMessage);

            if (this.PublicClientApplication == null)
                throw new NullReferenceException();

            // If the operating system has UI
            if (this.PublicClientApplication.IsUserInteractive())
            {
                if (PublicClientSingleton.Instance.UseEmbedded)
                {
                    return await this.PublicClientApplication.AcquireTokenInteractive(scopes)
                        .WithLoginHint(existingAccount?.Username ?? String.Empty)
                        .WithUseEmbeddedWebView(true)
                        .WithParentActivityOrWindow(PlatformConfig.Instance.ParentWindow)
                        .ExecuteAsync()
                        .ConfigureAwait(false);
                }
                else
                {
                    SystemWebViewOptions systemWebViewOptions = new SystemWebViewOptions();
#if IOS
                    // Hide the privacy prompt in iOS
                    systemWebViewOptions.iOSHidePrivacyPrompt = true;
#endif
                    return await this.PublicClientApplication.AcquireTokenInteractive(scopes)
                        .WithLoginHint(existingAccount?.Username ?? String.Empty)
                        .WithSystemWebViewOptions(systemWebViewOptions)
                        .WithParentActivityOrWindow(PlatformConfig.Instance.ParentWindow)
                        .ExecuteAsync()
                        .ConfigureAwait(false);
                }
            }

            // If the operating system does not have UI (e.g. SSH into Linux), you can fallback to device code, however this
            // flow will not satisfy the "device is managed" CA policy.
            return await this.PublicClientApplication.AcquireTokenWithDeviceCode(scopes, (dcr) =>
            {
                Console.WriteLine(dcr.Message);
                return Task.CompletedTask;
            }).ExecuteAsync().ConfigureAwait(false);
        }

        /// <summary>
        /// Removes the first signed-in user's record from token cache
        /// </summary>
        public async Task SignOutUserAsync()
        {
            var existingUser = await FetchSignedInUserFromCache().ConfigureAwait(false);
            await this.SignOutUserAsync(existingUser).ConfigureAwait(false);
        }

        /// <summary>
        /// Removes a given user's record from token cache
        /// </summary>
        /// <param name="user">The user.</param>
        public async Task SignOutUserAsync(IAccount user)
        {
            if (this.PublicClientApplication == null) return;

            await this.PublicClientApplication.RemoveAsync(user).ConfigureAwait(false);
        }

        /// <summary>
        /// Fetches the signed in user from MSAL's token cache (if available).
        /// </summary>
        /// <returns></returns>
        public async Task<IAccount> FetchSignedInUserFromCache()
        {
            Exception<NullReferenceException>.ThrowOn(() => this.PublicClientApplication == null, PCANotInitializedExceptionMessage);

            // get accounts from cache
            IEnumerable<IAccount> accounts = await this.PublicClientApplication.GetAccountsAsync().ConfigureAwait(false);

            // Error corner case: we should always have 0 or 1 accounts, not expecting > 1
            // This is just an example of how to resolve this ambiguity, which can arise if more apps share a token cache.
            // Note that some apps prefer to use a random account from the cache.
            if (accounts.Count() > 1)
            {
                foreach (var acc in accounts)
                {
                    await this.PublicClientApplication.RemoveAsync(acc);
                }

                return null;
            }

            return accounts.SingleOrDefault();
        }
    }
}

0 ответов

Другие вопросы по тегам