Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom browser handling #434

Merged
merged 6 commits into from
Jan 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions BREAKINGCHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,17 @@ ___Note:___ We might, and will probably, miss to document some of this - if so -

Breaking changes between version 8.0.0 and 7.0.0

### IBankIdLauncherCustomAppCallback becomes IBankIdLauncherCustomBrowser

We have broadedned the scope of the interface `IBankIdLauncherCustomAppCallback` to `IBankIdLauncherCustomBrowser` to allow for more flexibility in the implementation.

We have renamed these things as a result of that:
* `IBankIdLauncherCustomAppCallback` becomes `IBankIdLauncherCustomBrowser`
* `AddCustomAppCallback` becomes `AddCustomBrowser`
* `AddCustomAppCallbackByUserAgent` becomes `AddCustomBrowserByUserAgent`

Also, the return URL is now only applied on iOS, as the expected behaviour on Android is to apply null so that Android automatically can return to the previous app.

### Upgrade to .NET 7

We now require .NET 8 - so this requires you to upgrade your website that uses Active Login.
Expand Down
51 changes: 26 additions & 25 deletions docs/articles/bankid.md
Original file line number Diff line number Diff line change
Expand Up @@ -1077,12 +1077,14 @@ In Active Login device and browser detection is required for example to determin

The default implementation provided in `ActiveLogin.Authentication.BankId.AspNetCore` is limited to supports the ~top 5 most common browsers on both iOS and Android. But since an incorrect browser detection can lead to an incorrect launch URL and result in a broken user flow, `UAParserDeviceDetector` in the `ActiveLogin.Authentication.BankId.UAParser` package should be used to support additional browsers. It has a dependency on package [uap-csharp](https://github.com/ua-parser/uap-csharp) for improved user agent parsing.

#### Shorthand for only overriding return URL for custom apps
#### Shorthand for only overriding config for custom browsers

If you want to support your custom app, or a third party app (like the built in browsers in Instagram, Facebook etc.) we've made it simple to support those scenarios by allowing you to specify a custom return URL.
If you want to support your custom app, or a third party app (like the built in browsers in Instagram, Facebook etc.) we've made it simple to support those scenarios by allowing you to specify a custom browser config.

The most common scenario is that you will set the schema for the app as return URL if you detect a specific User Agent, so for that scenario we've made an extension method.

Note: The return url will onlt by applied on iOS, as Android will return the user to the app automatically.

In the sample below we add support for Instagram and Facebook:

```csharp
Expand All @@ -1091,29 +1093,43 @@ services
{
// ...

bankId.AddCustomAppCallbackByUserAgent(userAgent => userAgent.Contains("Instagram"), "instagram://");
bankId.AddCustomAppCallbackByUserAgent(userAgent => userAgent.Contains("FBAN") || userAgent.Contains("FBAV"), "fb://");
bankId.AddCustomBrowserByUserAgent(userAgent => userAgent.Contains("Instagram"), "instagram://");
bankId.AddCustomBrowserByUserAgent(userAgent => userAgent.Contains("FBAN") || userAgent.Contains("FBAV"), "fb://");

// ...
});
```

If you need, you can also specify the reload behaviour on the custom browser:

```csharp
services
.AddBankId(bankId =>
{
// ...

bankId.AddCustomBrowserByUserAgent(userAgent => userAgent.Contains("Instagram"), new BankIdLauncherUserAgentCustomBrowser("instagram://", BrowserReloadBehaviourOnReturnFromBankIdApp.Never));

// ...
});
```

If you need to do something custom, you can implement `IBankIdLauncherCustomAppCallback`:
If you need to do something custom, you can implement `IBankIdLauncherCustomBrowser`:

```csharp
services
.AddBankId(bankId =>
{
// ...

bankId.AddCustomAppCallback<BankIdFacebookAppCallback>();
bankId.AddCustomBrowser<BankIdFacebookAppBrowserConfig>();

// ...
});
```

```csharp
public class BankIdFacebookAppCallback : IBankIdLauncherCustomAppCallback
public class BankIdFacebookAppBrowserConfig : IBankIdLauncherCustomAppCallback
{
private readonly IHttpContextAccessor _httpContextAccessor;

Expand All @@ -1136,29 +1152,14 @@ public class BankIdFacebookAppCallback : IBankIdLauncherCustomAppCallback

public Task<string> GetCustomAppReturnUrl(BankIdLauncherCustomAppCallbackContext context)
{
return Task.FromResult("fb://");
return Task.FromResult(
new BankIdLauncherCustomAppCallbackResult("fb://", BrowserReloadBehaviourOnReturnFromBankIdApp.Never, BrowserMightRequireUserInteractionToLaunch.Default)
);
}
}

```

By default, Safari on iOS will reload when you return from the BankID
app. If the flow starts in a native iOS app, you want to turn off this
behavior by implementing
`IBankIdLauncherCustomAppCallback.ReloadPageOnReturnFromBankIdApp` if
you are using an embedded Web View in your app.

```csharp
public class BankIdFacebookAppCallback : IBankIdLauncherCustomAppCallback
{
...

public ReloadBehaviourOnReturnFromBankIdApp
ReloadPageOnReturnFromBankIdApp(BankIdSupportedDevice detectedDevice) =>
ReloadBehaviourOnReturnFromBankIdApp.Never;
}
```

### Verify digital ID card

To use the API for "Verify digital ID card" you first need to register the BankID services, select an environment etc.
Expand Down
4 changes: 2 additions & 2 deletions samples/Standalone.MvcSample/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@
bankId.UseQrCoderQrCodeGenerator();
bankId.UseUaParserDeviceDetection();

bankId.AddCustomAppCallbackByUserAgent(userAgent => userAgent.Contains("Instagram"), "instagram://");
bankId.AddCustomAppCallbackByUserAgent(userAgent => userAgent.Contains("FBAN") || userAgent.Contains("FBAV"), "fb://");
bankId.AddCustomBrowserByUserAgent(userAgent => userAgent.Contains("Instagram"), "instagram://");
bankId.AddCustomBrowserByUserAgent(userAgent => userAgent.Contains("FBAN") || userAgent.Contains("FBAV"), "fb://");

if (configuration.GetValue("ActiveLogin:BankId:UseSimulatedEnvironment", false))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,29 +14,43 @@ public static class IBankIdBuilderExtensions
/// </summary>
/// <param name="builder"></param>
/// <param name="isApplicable"></param>
/// <param name="returnUrl"></param>
/// <returns></returns>
public static IBankIdBuilder AddCustomBrowserByUserAgent(this IBankIdBuilder builder, Func<string, bool> isApplicable, string returnUrl)
{
return AddCustomBrowserByUserAgent(builder, isApplicable, context => returnUrl);
}

/// <summary>
/// Adds support for a custom browser (like a third party app).
/// When only returnUrl is specified, the reload behaviour will fall back to "Never". As we know, only Safari on iOS have the Always behaviour.
/// </summary>
/// <param name="builder"></param>
/// <param name="isApplicable"></param>
/// <param name="getReturnUrl"></param>
/// <returns></returns>
public static IBankIdBuilder AddCustomAppCallbackByUserAgent(this IBankIdBuilder builder, Func<string, bool> isApplicable, Func<BankIdLauncherCustomAppCallbackContext, string> getReturnUrl)
public static IBankIdBuilder AddCustomBrowserByUserAgent(this IBankIdBuilder builder, Func<string, bool> isApplicable, Func<BankIdLauncherCustomBrowserContext, string> getReturnUrl)
{
builder.Services.AddTransient<IBankIdLauncherCustomAppCallback>(x =>
{
var httpContextAccessor = x.GetRequiredService<IHttpContextAccessor>();
var customApp = new BankIdLauncherUserAgentCustomAppCallback(httpContextAccessor, isApplicable, getReturnUrl);
return customApp;
});
BankIdLauncherCustomBrowserConfig GetResult(BankIdLauncherCustomBrowserContext context) => new(getReturnUrl(context), BrowserReloadBehaviourOnReturnFromBankIdApp.Never);

return builder;
return AddCustomBrowserByUserAgent(builder, isApplicable, GetResult);
}

/// <summary>
/// Adds a custom return url resolver.
/// Adds support for a custom browser (like a third party app).
/// </summary>
/// <param name="builder"></param>
/// <param name="isApplicable"></param>
/// <param name="returnUrl"></param>
/// <param name="getResult"></param>
/// <returns></returns>
public static IBankIdBuilder AddCustomAppCallbackByUserAgent(this IBankIdBuilder builder, Func<string, bool> isApplicable, string returnUrl)
public static IBankIdBuilder AddCustomBrowserByUserAgent(this IBankIdBuilder builder, Func<string, bool> isApplicable, Func<BankIdLauncherCustomBrowserContext, BankIdLauncherCustomBrowserConfig> getResult)
{
return AddCustomAppCallbackByUserAgent(builder, isApplicable, context => returnUrl);
builder.Services.AddTransient<IBankIdLauncherCustomBrowser>(x =>
{
var httpContextAccessor = x.GetRequiredService<IHttpContextAccessor>();
return new BankIdLauncherCustomBrowserByUserAgent(httpContextAccessor, isApplicable, getResult);
});

return builder;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@

namespace ActiveLogin.Authentication.BankId.AspNetCore.Launcher;

public class BankIdLauncherUserAgentCustomAppCallback : IBankIdLauncherCustomAppCallback
public class BankIdLauncherCustomBrowserByUserAgent : IBankIdLauncherCustomBrowser
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly Func<string, bool> _isApplicable;
private readonly Func<BankIdLauncherCustomAppCallbackContext, string> _getReturnUrl;
private readonly Func<BankIdLauncherCustomBrowserContext, BankIdLauncherCustomBrowserConfig> _getResult;

public BankIdLauncherUserAgentCustomAppCallback(IHttpContextAccessor httpContextAccessor, Func<string, bool> isApplicable, Func<BankIdLauncherCustomAppCallbackContext, string> getReturnUrl)
public BankIdLauncherCustomBrowserByUserAgent(IHttpContextAccessor httpContextAccessor, Func<string, bool> isApplicable, Func<BankIdLauncherCustomBrowserContext, BankIdLauncherCustomBrowserConfig> getResult)
{
_httpContextAccessor = httpContextAccessor;
_isApplicable = isApplicable;
_getReturnUrl = getReturnUrl;
_getResult = getResult;
}

public Task<bool> IsApplicable(BankIdLauncherCustomAppCallbackContext context)
public Task<bool> IsApplicable(BankIdLauncherCustomBrowserContext context)
{
var userAgent = _httpContextAccessor.HttpContext?.Request.Headers.UserAgent.FirstOrDefault();
if (string.IsNullOrWhiteSpace(userAgent))
Expand All @@ -29,9 +29,9 @@ public Task<bool> IsApplicable(BankIdLauncherCustomAppCallbackContext context)
return Task.FromResult(isApplicable);
}

public Task<string> GetCustomAppReturnUrl(BankIdLauncherCustomAppCallbackContext context)
public Task<BankIdLauncherCustomBrowserConfig> GetCustomAppCallbackResult(BankIdLauncherCustomBrowserContext context)
{
var returnUrl = _getReturnUrl(context);
return Task.FromResult(returnUrl);
var result = _getResult(context);
return Task.FromResult(result);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -124,14 +124,28 @@ public static IBankIdBuilder AddResultStore<TImplementation>(this IBankIdBuilder
}

/// <summary>
/// Adds a class to resolve custom return url.
/// Adds a class to resolve custom browser config.
/// </summary>
/// <typeparam name="TImplementation"></typeparam>
/// <param name="builder"></param>
/// <returns></returns>
public static IBankIdBuilder AddCustomAppCallback<TImplementation>(this IBankIdBuilder builder) where TImplementation : class, IBankIdLauncherCustomAppCallback
public static IBankIdBuilder AddCustomBrowser<TImplementation>(this IBankIdBuilder builder) where TImplementation : class, IBankIdLauncherCustomBrowser
{
builder.Services.AddTransient<IBankIdLauncherCustomAppCallback, TImplementation>();
builder.Services.AddTransient<IBankIdLauncherCustomBrowser, TImplementation>();

return builder;
}

/// <summary>
/// Adds support for a custom browser (like a third party app).
/// </summary>
/// <param name="builder"></param>
/// <param name="isApplicable"></param>
/// <param name="getResult"></param>
/// <returns></returns>
public static IBankIdBuilder AddCustomBrowserByContext(this IBankIdBuilder builder, Func<BankIdLauncherCustomBrowserContext, bool> isApplicable, Func<BankIdLauncherCustomBrowserContext, BankIdLauncherCustomBrowserConfig> getResult)
{
builder.Services.AddTransient<IBankIdLauncherCustomBrowser>(x => new BankIdLauncherCustomBrowserByContext(isApplicable, getResult));

return builder;
}
Expand Down
Loading
Loading