-
Notifications
You must be signed in to change notification settings - Fork 255
Custom Signed Assertion Providers
JLoze edited this page Feb 7, 2025
·
8 revisions
Microsoft Identity Web allows the use of custom credential sources/identity providers by enabling extension of the built-in credential loaders with your own custom SDK implementation. You bring the signed credential; we do the rest.
A working sample can be found here in the Microsoft Identity Web repo which doubles as an integration test for this feature.
Building an extension to go on top of Microsoft Identity Web can be done with just three steps:
Step 1: Create an assertion provider class deriving from ClientAssertionProviderBase.
- The assertion provider takes in the properties and options needed to request a signed assertion from your custom source.
- The assertion provider then receives the signed assertion from your custom source and returns it.
- The sample file can be found here.
Example:
namespace MyCustomExtension
{
internal class MyCustomSignedAssertionProvider : ClientAssertionProviderBase
{
public MyCustomSignedAssertionProvider(Dictionary<string, object>? properties)
{
// Here you would implement the logic to extract what you need from the properties passed in
// the configuration
}
protected override Task<ClientAssertion> GetClientAssertionAsync(AssertionRequestOptions? assertionRequestOptions)
{
// Here you would implement the logic to get the signed assertion, which is probably going
// to be a call to a service. This call can be parameterized by the parameters in the properties
// of the constructor.
// In this sample code we just create an empty signed assertion and return it.
var clientAssertion = new ClientAssertion("FakeAssertion", DateTimeOffset.Now);
return Task.FromResult(clientAssertion);
}
}
}Step 2: Provide a signed assertion loader class ironically implementing ICustomSignedAssertionProvider.
- Your custom assertion loader will use instances of your custom assertion provider to get signed assertions from your custom source.
- Your loader will be called from the Microsoft Identity Web library through the LoadIfNeededAsync method for relevant credential descriptions.
- Your loader will need to call GetSignedAssertionAsync from your provider in order to get the signed assertion.
- If you successfully obtain a signed assertion, set the CredentialDescription.CachedValue to the new MyCustomSignedAssertionProvider.
- If there is an exception, set the skip value in the credential description to true to avoid trying to get the same credential again.
- It is ok to not get a credential, not all credentials are available in all contexts a given program runs in.
- Important: only throw the exception if you want the Microsoft Identity Web library to also throw the exception, likely crashing the program.
- The sample file can be found here.
Example:
namespace MyCustomExtension
{
internal class MyCustomSignedAssertionLoader : ICustomSignedAssertionProvider
{
public MyCustomSignedAssertionLoader(ILogger<DefaultCredentialsLoader> logger)
{
_logger = logger;
}
public CredentialSource CredentialSource => CredentialSource.CustomSignedAssertion;
public string Name => "MyCustomExtension";
private ILogger<DefaultCredentialsLoader> _logger;
public async Task LoadIfNeededAsync(CredentialDescription credentialDescription, CredentialSourceLoaderParameters? parameters = null)
{
MyCustomSignedAssertionProvider? signedAssertion = credentialDescription.CachedValue as MyCustomSignedAssertionProvider;
if (credentialDescription.CachedValue == null)
{
signedAssertion = new MyCustomSignedAssertionProvider(credentialDescription.CustomSignedAssertionProviderData);
}
try
{
// Given that not all credentials are available in all contexts (like managed identities not being available on local machines),
// we need to attempt to get a signed assertion, but if it fails, keep trying to find a working credential.
_ = await signedAssertion!.GetSignedAssertionAsync(null);
// Be sure to set the CachedValue in the CredentialDescription object to your signed assertion so you don't reevaluate
// the same credential more than is necessary.
credentialDescription.CachedValue = signedAssertion;
}
catch (Exception)
{
// Setting the skip to true will tell the program to no longer try loading credentials
// from this specific CredentialDescription object instance.
credentialDescription.Skip = true;
// Only throw the Exception if you want it to also be thrown by the Microsoft Identity Web library.
// Use the logger if you only want to log it.
throw;
}
}
}
}- This is what enables Microsoft Identity Web to find your custom loader
- This is the only thing a developer using your extension will need to call in order to use it
- The sample file can be found here.
Example:
public static IServiceCollection AddCustomSignedAssertionProvider(
this IServiceCollection services)
{
services.AddSingleton<ICustomSignedAssertionProvider, MyCustomSignedAssertionLoader>();
return services;
}For a user to use your extension two things need to happen.
- In the example below this is done using the call to tokenAcquirerFactory.Services.Configure
- This can instead be done in the appsettings.json which you can find an example of in the sample here
- Be sure to call it before building the factory.
- In the sample this is done in the test file here
TokenAcquirerFactory tokenAcquirerFactory = TokenAcquirerFactory.GetDefaultInstance();
tokenAcquirerFactory.Services.Configure<MicrosoftIdentityApplicationOptions>(options =>
{
options.Instance = "https://login.microsoftonline.com/";
options.TenantId = "msidlab4.onmicrosoft.com";
options.ClientId = "f6b698c0-140c-448f-8155-4aa9bf77ceba";
options.ClientCredentials = [ new CredentialDescription() { SourceType = CredentialSource.CustomSignedAssertion,
CustomSignedAssertionProviderName = "MyCustomExtension"}];
});
tokenAcquirerFactory.Services.AddCustomSignedAssertionProvider();
var serviceProvider = tokenAcquirerFactory.Build();- Home
- Why use Microsoft Identity Web?
- Web apps
- Web APIs
- Minimal support for .NET FW Classic
- Logging
- Azure AD B2C limitations
- Samples
- Certificates
- Managed Identity as Federated Credential
- Federated Credentials from other Identity Provider
- Extensibility: Bring your own credential
- Get client secrets from KeyVault
- Web apps
- Web app samples
- Web app template
- Call an API from a web app
- Managing incremental consent and conditional access
- Web app troubleshooting
- Deploy to App Services Linux containers or with proxies
- SameSite cookies
- Hybrid SPA
- Web APIs
- Web API samples
- Web API template
- Call an API from a web API
- Token Decryption
- Web API troubleshooting
- web API protected by ACLs instead of app roles
- gRPC apps
- Azure Functions
- Long running processes in web APIs
- Authorization policies
- Generic API
- Customization
- Logging
- Calling graph with specific scopes/tenant
- Multiple Authentication Schemes
- Utility classes
- Setting FIC+MSI
- Mixing web app and web API
- Deploying to Azure App Services
- Azure AD B2C issuer claim support
- Performance
- specify Microsoft Graph scopes and app-permissions
- Integrate with Azure App Services authentication
- Ajax calls and incremental consent and conditional access
- Back channel proxys
- Client capabilities