You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This design proposal aims at adding extensibility to DefaultCredentialsLoader so that partner teams, or SDK on top of Microsoft.Identity.Web can bring their own credential providers.
In that article, we mentioned that the developer using this extensibility provides CredentialDescription with a specific CustomSignedAssertionProviderName and calls an extension method on the service collection (for instance services.AddFmiProvider();). This design proposal explains how the extender provides
the extension method on the service collection.
the signed assertion credential source loader, and the signed assertion provider
the value that developers need to use in CustomSignedAssertionProviderName (possibly the data),
In scope
This design proposal is for the DevEx for the provider of the extension (that is the extender)
internalclassMyCustomSignedAssertionProvider:ClientAssertionProviderBase{publicMyCustomSignedAssertionProvider(Dictionary<string,object>?properties){// Here you would implement the logic to extract what you need from the properties passed in// the configuration}protectedoverrideTask<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.varclientAssertion=newClientAssertion("FakeAssertion",DateTimeOffset.Now);returnTask.FromResult(clientAssertion);}}
get from the cached value or instantiate the class in 1) (MyCustomSignedAssertionProvider ) in
the LoadIfNeededAsync overload
Call GetSignedAssertionAsync
process the exception and set the Skip flag. Set the CachedValue to the new MyCustomSignedAssertionProvider in case there is no exception.
namespaceExtensibilityTests{internalclassMyCustomSignedAssertionLoader:ICustomSignedAssertionProvider{publicMyCustomSignedAssertionLoader(ILogger<DefaultCredentialsLoader>logger){_logger=logger;}publicCredentialSourceCredentialSource=>CredentialSource.CustomSignedAssertion;publicstringName=>"MyCustomExtension";privateILogger<DefaultCredentialsLoader>_logger;publicasyncTaskLoadIfNeededAsync(CredentialDescriptioncredentialDescription,CredentialSourceLoaderParameters?parameters=null){MyCustomSignedAssertionProvider?signedAssertion=credentialDescription.CachedValueasMyCustomSignedAssertionProvider;if(credentialDescription.CachedValue==null){signedAssertion=newMyCustomSignedAssertionProvider(credentialDescription.CustomSignedAssertionProviderData);}try{// Given that managed identity can be not available locally, we need to try to get a// signed assertion, and if it fails, move to the next credentials_=awaitsignedAssertion!.GetSignedAssertionAsync(null);credentialDescription.CachedValue=signedAssertion;}catch(Exception){credentialDescription.Skip=true;throw;}}}}
Write an extension method to register the extension with the service collection. This is the only class the developer
using the extension will need to use.
In Microsoft.Identity.Web.DefaultCredentialsLoader (in the Microsoft.Identity.Web.Certificate)
Add a public property CustomSignedAssertionCredentialSourceLoaders of type IDictionary<string, ICustomSignedAssertionProvider>
/// <summary>/// Dictionary of custom signed assertion credential source loaders, by name (fully qualified type name)./// </summary>publicIDictionary<string,ICredentialSourceLoader>CustomSignedAssertionCredentialSourceLoaders{get;}
Add an extra parameter to the constructor of DefaultCredentialLoader: signedAssertionProviders
of type IEnumerable<ICustomSignedAssertionProvider>? with a default value of null, to avoid a breaking change.
Initialize CustomSignedAssertionCredentialSourceLoaders it in the constructor of DefaultCredentialsLoader based
on the values in signedAssertionProviders. For each provider, the key would be provider.Name ?? provider.GetType().FullName!
In the LoadCredentialsIfNeededAsync method, when the CachedValue is null, if the sourceType of the credential
description is CredentialSource.CustomSignedAssertion and credentialDescription.CustomSignedAssertionProviderName
is defined, instead of using the CredentialsSourceLoaders, use the CustomSignedAssertionSourceLoaders. The code will
be very similar in both cases (custom providers or in the box providers).
If the CustomSignedAssertionCredentialSourceLoaders of name
credentialDescription.CustomSignedAssertionProviderName is not found, we need to log a new error
(CustomSignedAssertionProviderNotFound):
"Failed to find custom signed assertion provider {name} from source {sourceType}. Will it be skipped in the future ? {skip}.");
Modify the DefaultCredentialLoader.Logger.cs file to add the new log message.
In Microsoft.Identity.Web.DefautCertificateLoader
Add the same kind of constructor in DefaultCertificateLoader (calling the constructor of the base class). This will
be used by the Dependency injection to inject the ICustomSignedAssertionProvider (custom providers)
In ConfidentialClientApplicationBuilderExtension, in LoadCredentialForMsalOrFailAsync:
process the case where credential.SourceType == CredentialSource.CustomSignedAssertion (if not skiped, return the credentials). and add
logging
Unit tests
Integration tests.
Create an integration test that demonstrate the extension (MyCustomSignedAssertionProvider)
and the way to use it. (for instance with a daemon configuration like the following, but that's just an example)
TokenAcquirerFactorytokenAcquirerFactory=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=[newCredentialDescription(){SourceType=CredentialSource.CustomSignedAssertion,CustomSignedAssertionProviderName="MyCustomExtension"}];});tokenAcquirerFactory.Services.AddCustomSignedAssertionProvider();varserviceProvider=tokenAcquirerFactory.Build();// Get the authorization request creator serviceIAuthorizationHeaderProviderauthorizationHeaderProvider=serviceProvider.GetRequiredService<IAuthorizationHeaderProvider>();stringauthorizationHeader=awaitauthorizationHeaderProvider.CreateAuthorizationHeaderForAppAsync("https://graph.microsoft.com/.default");Console.WriteLine(authorizationHeader.Substring(0,authorizationHeader.IndexOf(" ",StringComparison.OrdinalIgnoreCase)+4)+"...");}
Add documentation
Add a wiki article to document the extensibility (probably based on the and this design proposal)
The text was updated successfully, but these errors were encountered:
Summary
This design proposal aims at adding extensibility to DefaultCredentialsLoader so that partner teams, or SDK on top of Microsoft.Identity.Web can bring their own credential providers.
See also AzureAD/microsoft-identity-abstractions-for-dotnet#146 and AzureAD/microsoft-identity-abstractions-for-dotnet#153 which are pre-requisites
Motivation and goals
See Add extensibility to CredentialDescription to support custom signed assertion providers for the devex for users of the extensibility.
In that article, we mentioned that the developer using this extensibility provides
CredentialDescription
with a specificCustomSignedAssertionProviderName
and calls an extension method on the service collection (for instanceservices.AddFmiProvider();
). This design proposal explains how the extender providesCustomSignedAssertionProviderName
(possibly the data),In scope
Out of scope
Proposed developer experience for the extender providing a custom assertion provider
Remember, the developer experience for the developer using the extension is described in Add extensibility to CredentialDescription to support custom signed assertion providers
Configuration file
Nothing to do. This is not about the developer using the extensibility
Startup.cs
Nothing to do. This is not about the developer using the extensibility
Classes provided as the extension
The developer providing the extension needs to provide three classes:
A class deriving from ClientAssertionProviderBase and that will compute or retrieve the signed assertion.
Exemple:
Provide a class implementing ICustomSignedAssertionProvider which needs to:
the LoadIfNeededAsync overload
Write an extension method to register the extension with the service collection. This is the only class the developer
using the extension will need to use.
Design/Work to be done in Microsoft.Identity.Web
In Microsoft.Identity.Web.DefaultCredentialsLoader (in the Microsoft.Identity.Web.Certificate)
Add a public property
CustomSignedAssertionCredentialSourceLoaders
of typeIDictionary<string, ICustomSignedAssertionProvider>
Add an extra parameter to the constructor of DefaultCredentialLoader: signedAssertionProviders
of type
IEnumerable<ICustomSignedAssertionProvider>?
with a default value of null, to avoid a breaking change.Initialize CustomSignedAssertionCredentialSourceLoaders it in the constructor of DefaultCredentialsLoader based
on the values in signedAssertionProviders. For each provider, the key would be
provider.Name ?? provider.GetType().FullName!
In the LoadCredentialsIfNeededAsync method, when the CachedValue is null, if the sourceType of the credential
description is CredentialSource.CustomSignedAssertion and credentialDescription.CustomSignedAssertionProviderName
is defined, instead of using the CredentialsSourceLoaders, use the CustomSignedAssertionSourceLoaders. The code will
be very similar in both cases (custom providers or in the box providers).
If the CustomSignedAssertionCredentialSourceLoaders of name
credentialDescription.CustomSignedAssertionProviderName is not found, we need to log a new error
(CustomSignedAssertionProviderNotFound):
Modify the DefaultCredentialLoader.Logger.cs file to add the new log message.
In Microsoft.Identity.Web.DefautCertificateLoader
be used by the Dependency injection to inject the ICustomSignedAssertionProvider (custom providers)
In ConfidentialClientApplicationBuilderExtension, in LoadCredentialForMsalOrFailAsync:
logging
Unit tests
Integration tests.
Create an integration test that demonstrate the extension (MyCustomSignedAssertionProvider)
and the way to use it. (for instance with a daemon configuration like the following, but that's just an example)
Add documentation
Add a wiki article to document the extensibility (probably based on the and this design proposal)
The text was updated successfully, but these errors were encountered: