IdentityModel.OidcClient icon indicating copy to clipboard operation
IdentityModel.OidcClient copied to clipboard

Not passing StartUrl to Browser in .net-ios

Open JimWilcox3 opened this issue 9 months ago • 1 comments

I have upgraded my Xamarin apps to .net8.0-ios rather than NetMaui. .net8.0-ios is very much supported now and will be in the future. When I upgrade IdentityModel.OidcClient to version 6.0.0, when it makes the call to open the Browser, it no longer passes a StartUrl. The parameter is null. My Browser is implemented as follows.

 public class ASWebAuthenticationSessionBrowser : IOSBrowserBase
  {
      public ASWebAuthenticationSessionOptions SessionOptions { get; }

      public ASWebAuthenticationSessionBrowser(ASWebAuthenticationSessionOptions sessionOptions = null)
      {
          SessionOptions = sessionOptions;
      }

      /// <inheritdoc/>
      protected override Task<BrowserResult> Launch(BrowserOptions options, CancellationToken cancellationToken = default)
      {
          return Start(options, SessionOptions);
      }

      internal static Task<BrowserResult> Start(BrowserOptions options, ASWebAuthenticationSessionOptions sessionOptions = null)
      {
          var tcs = new TaskCompletionSource<BrowserResult>();

          ASWebAuthenticationSession asWebAuthenticationSession = null;
          asWebAuthenticationSession = new ASWebAuthenticationSession(
              new NSUrl(options.StartUrl),
              new NSUrl(options.EndUrl).Scheme,
              (callbackUrl, error) =>
              {
                  tcs.SetResult(CreateBrowserResult(callbackUrl, error));
                  asWebAuthenticationSession.Dispose();
              });

          if (UIDevice.CurrentDevice.CheckSystemVersion(13, 0))
          {
              // iOS 13 requires the PresentationContextProvider set
              asWebAuthenticationSession.PresentationContextProvider = new PresentationContextProviderToSharedKeyWindow();
              // PrefersEphemeralWebBrowserSession is only available on iOS 13 and up.
              asWebAuthenticationSession.PrefersEphemeralWebBrowserSession = sessionOptions != null ? sessionOptions.PrefersEphemeralWebBrowserSession : false;
          }

          asWebAuthenticationSession.Start();

          return tcs.Task;
      }

      class PresentationContextProviderToSharedKeyWindow : NSObject, IASWebAuthenticationPresentationContextProviding
      {
          public UIWindow GetPresentationAnchor(ASWebAuthenticationSession session)
          {
              return UIApplication.SharedApplication.KeyWindow;
          }
      }

      private static BrowserResult CreateBrowserResult(NSUrl callbackUrl, NSError error)
      {
          if (error == null)
              return Success(callbackUrl.AbsoluteString);

          if (error.Code == (long)ASWebAuthenticationSessionErrorCode.CanceledLogin)
              return Canceled();

          return UnknownError(error.ToString());
      }
  }
   public abstract class IOSBrowserBase : IBrowser
   {
       public Task<BrowserResult> InvokeAsync(BrowserOptions options, CancellationToken cancellationToken = default)
       {
           if (string.IsNullOrWhiteSpace(options.StartUrl))
               throw new ArgumentException("Missing StartUrl", nameof(options));

           if (string.IsNullOrWhiteSpace(options.EndUrl))
               throw new ArgumentException("Missing EndUrl", nameof(options));

           return Launch(options, cancellationToken);
       }

       protected abstract Task<BrowserResult> Launch(BrowserOptions options, CancellationToken cancellationToken = default);

       internal static BrowserResult Canceled()
       {
           return new BrowserResult
           {
               ResultType = BrowserResultType.UserCancel
           };
       }

       internal static BrowserResult UnknownError(string error)
       {
           return new BrowserResult
           {
               ResultType = BrowserResultType.UnknownError,
               Error = error
           };
       }

       internal static BrowserResult Success(string response)
       {
           return new BrowserResult
           {
               Response = response,
               ResultType = BrowserResultType.Success
           };
       }
   }

I Use the following code to invoke a login:

  _options = new OidcClientOptions
  {
      Authority = vm.Config["Settings:Authority"],
      ClientId = vm.Config["Settings:ClientId"],
      Scope = vm.Config["Settings:Scope"],
      RedirectUri = vm.Config["Settings:RedirectUri"],
      PostLogoutRedirectUri = vm.Config["Settings:RedirectUri"],
      Browser = new ASWebAuthenticationSessionBrowser(new ASWebAuthenticationSessionOptions
      {
          PrefersEphemeralWebBrowserSession = true
      })
  };

  var oidcClient = new OidcClient(_options);

  try
  {
      var result = await oidcClient.LoginAsync();

      //Use the following in place of the above if you want to force a login
      //var parm = new Parameters
      //{
      //    { "prompt", "login" }
      //};
      //var result = await oidcClient.LoginAsync(new LoginRequest { FrontChannelExtraParameters = parm });

      if (!result.IsError)
      {
          vm.Tokens.AccessToken = result.AccessToken;
          vm.Tokens.IdentityToken = result.IdentityToken;
          vm.Tokens.RefreshToken = result.RefreshToken;
          vm.Tokens.AccessTokenExpiration = result.AccessTokenExpiration.DateTime.ToUniversalTime();
          vm.Tokens.Error = result.Error;
          //vm.Tokens.User = result.User;

          vm.Logger.LogInformation("{0} logged in!", result.User.Identity.Name);
      }
  }
  catch (Exception ex)
  {
      var s = ex.ToString();
  }

It works fine with version 5.2.1 but breaks in version 6.0.0. Is there anything I can do other than rewrite my app in NetMaui? I do have some paid time left with Duende if I need to use that to resolve this.

Thanks, Jim

JimWilcox3 avatar May 23 '24 15:05 JimWilcox3