Git Product home page Git Product logo

identitymodel.aspnetcore's Introduction

IdentityModel.AspNetCore

ASP.NET Core helper library managing user and client access tokens in ASP.NET Core.

important we have moved all active development of this library to this repo. You can read more about the rationale on this blog post.

identitymodel.aspnetcore's People

Contributors

adamralph avatar brockallen avatar darrenschwarz avatar dependabot-preview[bot] avatar dependabot[bot] avatar duckblaster avatar flcdrg avatar leastprivilege avatar mahgo avatar markgould avatar mohammadshahabrafiq avatar mvput avatar rmja avatar rodneykendall avatar slang25 avatar virustrinity avatar vladdy-moses avatar zyofeng avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

identitymodel.aspnetcore's Issues

`CacheLifetimeBuffer` past date exception

I use a specific web service who generates a JWT access token but does something odd. They are returning the same JWT for each subsequent request until its expired.

This leads to a cache expiration date cannot be in the past exception. Setting CachelifetimeBuffer to 0 resolves this however loses the benefit of refreshing early for other oauth resources I consume.

System.InvalidOperationException: The absolute expiration value must be in the future.

Unable to resolve service for type 'AccessTokenManagement.TokenEndpointService' while attempting to activate 'IdentityModel.AspNetCore.AccessTokenManagement.AccessTokenManagementService'.

Receiving following exception in code:

Unable to resolve service for type 'IdentityModel.AspNetCore.AccessTokenManagement.TokenEndpointService' while attempting to activate 'IdentityModel.AspNetCore.AccessTokenManagement.AccessTokenManagementService'.

Problem resolved by add TokenEndpointService in DI.
services.AddHttpClient<TokenEndpointService>();

Access token not included on connection. Blazor WebAssembly app.

Sorry to open an issue. This could be a configuration problem, but before I continue, I wanted to ask "Is this even supported?"

I have working code I copied from a .Net Core console app into this Blazor app, to use the Blazor as the front-end.

I used fiddler to confirm the token is retrieved. It is just not included as a bearer token on the request that requires authentication.

None of this is internet-facing. It's all back-end code for internal systems.

255_Full.txt

public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add("app");

        builder.Services.AddDistributedMemoryCache();

        string devAvatar = "DevAvatar";

        builder.Services.AddAccessTokenManagement(options =>
        {
            options.Client.Clients.Add(devAvatar, new ClientCredentialsTokenRequest
            {
                Address = "https://devauth/identity/connect/token",
                ClientId = "conveyable-restock-api",
                ClientSecret = "P^1IVpH@%1^mR7v@",
                Scope = "hl:conveyable-restock-api:driver"
            });
        });

        builder.Services.AddHttpClient("DevDisplayApi", configureClient: client =>
        {
            client.BaseAddress = new Uri("https://devservices/ConveyableRestock/api/Display");
        });

        builder.Services.AddClientAccessTokenClient("DevDriverApi", tokenClientName: devAvatar, configureClient: client =>
        {
            client.BaseAddress = new Uri("https://devservices/ConveyableRestock/secureapi/");
        });

        await builder.Build().RunAsync();
    }

Null reference exception that can be prevented

The following code can cause a null reference exception if the userToken returned is null:

    public async Task RevokeRefreshTokenAsync()
    {
        var userToken = await _userTokenStore.GetTokenAsync(_httpContextAccessor.HttpContext.User);

        if (!string.IsNullOrEmpty(userToken.RefreshToken))
        {
            var response = await _tokenEndpointService.RevokeRefreshTokenAsync(userToken.RefreshToken);

            if (response.IsError)
            {
                _logger.LogError("Error revoking refresh token. Error = {error}", response.Error);
            }
        }
    }

I think it can be:

    public async Task RevokeRefreshTokenAsync()
    {
        var userToken = await _userTokenStore.GetTokenAsync(_httpContextAccessor.HttpContext.User);

        if (!string.IsNullOrEmpty(userToken?.RefreshToken))
        {
            var response = await _tokenEndpointService.RevokeRefreshTokenAsync(userToken.RefreshToken);

            if (response.IsError)
            {
                _logger.LogError("Error revoking refresh token. Error = {error}", response.Error);
            }
        }
    }

without any negative side effects.

Kind regards,

Wesley

CancellationToken not propagated all the way

In ClientAccessTokenHandler doesn't propagate the cancellation token to the underlining implementation that fetches the token from STS.
Problem is that SetTokenAsync doesn't take cancellationToken

protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
await SetTokenAsync(request, forceRenewal: false);
var response = await base.SendAsync(request, cancellationToken);

More than happy to send a PR

Default value for ClientCredentialStyle should be ClientCredentialStyle.AuthorizationHeader

By default when client request is constructed ClientCredentialStyle set to Body:

//ProtocolRequest.cs
//line 65
public ClientCredentialStyle ClientCredentialStyle { get; set; } = ClientCredentialStyle.PostBody;

According to spec: https://tools.ietf.org/html/rfc6749#section-2.3.1

Including the client credentials in the request-body using the two
parameters is NOT RECOMMENDED and SHOULD be limited to clients unable
to directly utilize the HTTP Basic authentication scheme (or other
password-based HTTP authentication schemes).

Should default setting be ClientCredentialStyle.AuthorizationHeader?

AccessTokenManagementService exception management

In its current implementation, AccessTokenManagementService suppresses problems encountered when trying to obtain a token. e.g.:

if (response.IsError)
{
_logger.LogError("Error requesting access token for client {clientName}. Error = {error}", clientName, response.Error);
return null;
}

This can make it difficult for consuming developers to identify the cause of failures that occur when performing HTTP requests that should have been (but are not) decorated with authorization headers.

Is there a use case for suppressing these problems instead of throwing exceptions? If so, might it worthwhile to throw by default and add an option to allow suppression when it is actually desirable?

Issue with using await and continue with

Good Afternoon,

I was wondering if anyone else has run into this error: I have a list of objects (classes with several public properties) and I an trying to iterate over them and call a method on my API async-ly and then use ContinueWith to call the API again (different method) to do some db maintenance in order (I am updating a table and when that is done, I am updating a different table). But I keep getting a null reference exception in the GetUserAccessTokenAsync method.

Here is a section of my error log. (I changed the namespace to fit my application - TcrWeb)

2020-02-06 15:36:45.163 -08:00 [Information] Message Added: 2/6/2020 3:36:45 PM, Log ID: 33, AccessToken: eyJhbGciOiJSUzI1NiIsImtpZCI6Ijg1NDc0REJFQkZENUU4MDlFQjhFODZCRTExRUZFNDAwNjA4REM2QTQiLCJ0eXAiOiJhdCtqd3QiLCJ4NXQiOiJoVWROdnJfVjZBbnJqb2EtRWVfa0FHQ054cVEifQ.eyJuYmYiOjE1ODEwMzIxOTgsImV4cCI6MTU4MTAzNTc5OCwiaXNzIjoiaHR0cHM6Ly90Y3Itc3RnLmhjcG52LmNvbSIsImF1ZCI6InRjci1hcGkiLCJjbGllbnRfaWQiOiJ0Y3Itd2ViIiwic3ViIjoiMiIsImF1dGhfdGltZSI6MTU4MTAyNzMxOSwiaWRwIjoibG9jYWwiLCJuYW1lIjoiVGltIFNhdmFnZSIsInByZWZlcnJlZF91c2VybmFtZSI6InRpbW90aHkuc2F2YWdlIiwiZW1haWwiOiJ0c2F2YWdlQGhjcG52LmNvbSIsInJvbGVfbGlzdCI6InN1cGVyX3VzZXIiLCJ1c2VyX2lkIjoiMiIsImZ1bGxfbmFtZSI6IlRpbSBTYXZhZ2UiLCJjbGluaWNfbGlzdCI6IjM0LDM3LDM4LDUwLDUxLDUyLDU1LDU2LDU3LDU4LDU5LDYxLDYzLDY0LDY2LDY5LDcwLDcyLDc0LDc1LDc2LDc3LDc5LDgwLDgxLDgyLDgzLDg3LDg5LDkyLDkzLDk1LDk2LDk3LDEwMSwxMDMsMTc1LDE3OCwxODUsMTg2LDE4OSwxOTAsMTkzLDE5NCwxOTcsMjAyLDIwNCwyMDUsMjA2LDIwNywyMTAsMjI2LDIyNywyMjgsMjI5LDIzMSwyMzMsMjM1LDEsMywxMywxNCwxNiwxOCwxOSwyMCwyMSwyMiwyMywyOSwzMSIsImNsaW5pY19pZCI6IjM0IiwibGFzdF9sb2dpbiI6IjAyLzA2LzIwMjAgMTQ6MTU6MTkiLCJhcF9hcHByb3ZhbF9sZXZlbCI6IjQwIiwidXNlcl9zZXNzaW9uX2lkIjoiMzc2NmRlNmQtZTY0My00NjRmLThiOTQtMTQxMTljMGJlMTNkIiwiYWRkcmVzcyI6IjcwMCBFIFdhcm0gU3ByaW5ncyBSZC4gTGFzIFZlZ2FzLCBOViA4OTExOSIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL25hbWUiOiJ0aW1vdGh5LnNhdmFnZSIsInNjb3BlIjpbInByb2ZpbGUiLCJvcGVuaWQiLCJ0Y3ItYXBpIiwib2ZmbGluZV9hY2Nlc3MiXSwiYW1yIjpbInB3ZCJdfQ.oujbYTtsZy60XUoHECeP2bEys2wAYwlTHrfjVmLFAKuN9u67ZXnHpV9iNfbS1R69qEisJsrE7ZPh-WIVw73VxiEZyjHMeAbZ2MmEmJKRSUcq0fRwSi70gyQarjgGmir1_EtQvskLGGHyYaLV3Mpuqbv4HTJy7_94P_rrhj7kYu-w_2E1614pK7LeP0PKJbK22jcWdGrXVsqSRPcBCdQP9DQuLlyu5hZD-ecC0jqkQtAAsK_qAQvmy3jiv00z_KNyYLe4nmznPm1tP9PCltQmY6a31j-ZK7gCfMQkh69Rmxaum5o1r7URG7HXem9a-8eKC3ljNW2GIxyOb3z1uuTRjw
Checking if Client is available: True
(THIS LINE IS WHAT MY OBJECT HOLDS)
Encounter Log: Id:33,Campaign:2010,comment:,LogStatus:3,UserId:2,DateOfDischarge:
Error: Object reference not set to an instance of an object. -- StackTrace: at TcrWeb.TokenManagementHttpContextExtensions.GetUserAccessTokenAsync(HttpContext context, Boolean forceRenewal) in C:\Code\WebApps\TcrWeb\TcrWeb\AccessTokenManagement\TokenManagementHttpContextExtensions.cs:line 23
at TcrWeb.UserAccessTokenHandler.SetTokenAsync(HttpRequestMessage request, Boolean forceRenewal) in C:\Code\WebApps\TcrWeb\TcrWeb\AccessTokenManagement\UserAccessTokenHandler.cs:line 54
at TcrWeb.UserAccessTokenHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) in C:\Code\WebApps\TcrWeb\TcrWeb\AccessTokenManagement\UserAccessTokenHandler.cs:line 31
at Microsoft.Extensions.Http.Logging.LoggingScopeHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.FinishSendAsyncBuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)
at TcrWeb.EncounterDbService.MaintainEncounter(EncounterLog log, String accessToken) in C:\Code\WebApps\TcrWeb\TcrWeb\Services\EncounterDbService.cs:line 166

2020-02-06 15:37:21.511 -08:00 [Information] "Cookies" was not authenticated. Failure message: "Unprotect ticket failed"
2020-02-06 15:37:21.515 -08:00 [Information] AuthenticationScheme: "oidc" was challenged.

If you need anything else from me, please don't hesitate to let me know.

Thank you ,
Tim

Using another name as 'default' for token client throws exception?

I've this code:

.ConfigureServices((hostContext, services) =>
{
    services.AddAccessTokenManagement(options =>
    {
        options.Client.Clients.Add("AzureAD", new ClientCredentialsTokenRequest
        {
            Address = $"https://login.microsoftonline.com/{ten}/oauth2/token",
            ClientId = id,
            ClientSecret = secret,
            GrantType = "client_credentials",

            Scope = Scope1,

            Parameters =
            {
                { "resource", "821eb724-edb8-4dba-b425-3f953250c0ae" }
            }
        });
    });

    // custom name is used here:
    services.AddClientAccessTokenClient("client", "my-token-client", configureClient: client =>
    {
        client.BaseAddress = new Uri("https://localhost:5001");
    });

    services.AddHostedService<Worker>();
});

And the code to send the http get request:

var client = _clientFactory.CreateClient("client");
var response = await client.GetAsync("weatherforecast", stoppingToken);
[12:11:58 DBG] Cache miss for access token for client: my-token-client
Unhandled exception. System.InvalidOperationException: No access token client configuration found for client: my-token-client
   at IdentityModel.AspNetCore.AccessTokenManagement.DefaultTokenClientConfigurationService.GetClientCredentialsRequestAsync(String clientName)
   at IdentityModel.AspNetCore.AccessTokenManagement.TokenEndpointService.RequestClientAccessToken(String clientName)
   at IdentityModel.AspNetCore.AccessTokenManagement.AccessTokenManagementService.<>c__DisplayClass9_0.<<GetClientAccessTokenAsync>b__1>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at IdentityModel.AspNetCore.AccessTokenManagement.AccessTokenManagementService.GetClientAccessTokenAsync(String clientName, Boolean forceRenewal)
   at IdentityModel.AspNetCore.AccessTokenManagement.ClientAccessTokenHandler.SetTokenAsync(HttpRequestMessage request, Boolean forceRenewal)
   at IdentityModel.AspNetCore.AccessTokenManagement.ClientAccessTokenHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at Microsoft.Extensions.Http.Logging.LoggingScopeHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)

Nuget package release cycle

Hi there,

you are responding pretty quickly, thanks for that but it seems like you are only processing "open" elements in your workflow as i didn't get a response to my question i've asked after my PR got closed, see #121 (comment).

So once again, perhaps my question is of interest for others too:

When will changes made to the repo be included in a new nuget package? What is your current nuget package release cycle?

Currently i'm working with a git submodule pointing to my fork of your repo but that's not ideal.

So lonG
Daniel

AddAccessTokenManagement doesn't recalculate ClientAssertion.Value.

Problem with token client and ClientAssertion method.
This is how token management registered:

services.AddAccessTokenManagement(o =>
            {
                var tokenGenerator= services.BuildServiceProvider().GetRequiredService<PrivateKeyJwtTokenGenerator>();
                o.Client.Clients.Add(new ClientCredentialsTokenRequest
                {
                    Address = TokenEndpoint,
                    ClientAssertion = new ClientAssertion()
                    {
                        Type = OidcConstants.ClientAssertionTypes.JwtBearer,
                        Value = tokenGenerator.CreateClientAuthJwt()
                    }
                });
            });

It works... till token expires. AccessTokenManagement is trying to fetch new token - it is good, but!
ClientAssertion property is holding same object.

Problem here is that ClientAssertion.Value has same JWT as with initial request. It is not good, as this JWT is created with unique jti claim, and it means that this jwt supposed to be used only once.
https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication
As result token server rejects request for another token.

IdentityModel and Azure Functions V3 - Missing WebJobsAuthLevel authentication handler

Hi,
I've an Azure Function that doesnt need to be authenticated.
But after I registred the AddAccessTokenManagement and AddClientAccessTokenClient in the IoC, i got the following error:

image

Here is how i've implemented the function startup code:

image

If i remove the piece of code, everything backs to works perfectly.

Is there something that i'm missing?

Thanks

grant_type

Besides the open issue/feature request for custom grant_types, I was wondering how to add support for grant_type "password". Which is within the OAuth specification as far as I can tell (I'm not at all an expert)

Looking through the source I think I can see it being partially supported by the IdentityModel project. But I'm not capable of implementing this quickly.

And if I set the GrantType manually it gets overwritten in
HttpClientTokenRequestExtensions.RequestClientCredentialsTokenAsync

As I currently only need to change that, I was wondering if there is a quick way to override that behaviour.

"Unable to create the SignatureProvider" when using HMAC 256

Hiya,

Even though HMAC is listed under the supported algorithms here: https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/wiki/Supported-Algorithms

when I try to use HMAC for code (basically just the sample app code against a FusionAuth IdP), I get an exception:

Exceptions caught:
 'System.NotSupportedException: IDX10634: Unable to create the SignatureProvider.
Algorithm: 'HS256', SecurityKey: 'Microsoft.IdentityModel.Tokens.RsaSecurityKey, KeyId: 'T06oiDoC5J_5OFCoFT-MSMsoTuY', InternalId: 'db1d80f3-2b6c-419c-9b4a-7aa45659770a'.'
 is not supported. The list of supported algorithms is available here: https://aka.ms/IdentityModel/supported-algorithms
   at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateSignatureProvider(SecurityKey key, String algorithm, Boolean willCreateSignatures)
   at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateForVerifying(SecurityKey key, String algorithm)
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(Byte[] encodedBytes, Byte[] signature, SecurityKey key, String algorithm, TokenValidationParameters validationParameters)

If I use an RSA key to sign my JWTs, it works swimmingly.

I googled around a bit and couldn't find a good way to update the SignatureProvider, except this post, which seemed overkill: https://www.scottbrady91.com/C-Sharp/Supporting-Custom-JWT-Signing-Algorithms-in-dotnet-Core

Any suggestions on other things I should google?

Thanks!

Unable to load OpenID configuration for configured scheme

I am migrating an API to dotnet core 3.1. I also decided to start using this version of the IdentityModel. Here is the configuration of the API:

services.AddAuthentication(o =>
            {
                o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(o =>
           {
               string authority = Configuration["idsrv_authority"];
               o.Authority = authority;
               o.Audience = authority + "/resources";
               o.RequireHttpsMetadata = true;
           });

I have other HTTP/AP/RESTI services hosted externally. I am trying to configure an HttpClient to hit one of them using client credentials. Here is my HttpClient configuration:

            services
                .AddHttpClient<MyHttpClient>("ng", client => client.BaseAddress = apiAddress);
            services
                .AddAccessTokenManagement(options =>
                options.Client.Clients.Add("identityserver", new ClientCredentialsTokenRequest()
                {
                    Address = $"{authority}/connect/token",
                    ClientId = clientId,
                    ClientSecret = clientSecret
                })); ;

            services.AddClientAccessTokenClient("ng", configureClient: client =>
            {
                client.BaseAddress = apiAddress;
            });

When I try to hit an endpoint I get the following error:

System.InvalidOperationException: 'Unable to load OpenID configuration for configured scheme: Object reference not set to an instance of an object.'

I have figured that if I add the following config and point the default challenge to "oidc" it starts doing the right thing. It tries to request a token at least.

.AddOpenIdConnect("oidc", o =>
             {
                 string authority = Configuration["idsrv_authority"];
                 o.Authority = authority;
                 o.ClientId = "a";
                 o.ClientSecret = "a";
             })

However it seems very strange to have such configuration in the startup with AddOpenIdConnect(...). I already have specified to the client where is the client credentials address, the clientId and the secret inside the ClientCredentialsTokenRequest.

I guess I am doing something fundamentally wrong here. My question is what I have to do so that I can get an access token and hit the external API without adding AddOpenIdConnect(...)?

Milestone

What are the milestones for a 1.0.0 release? I would love to help if time permits.

Azure Functions v3 - Client Access Tokens

Hello,

I am having trouble following the v3 samples and configuring my function app to use the client access token handler.

The FunctionsStartup.Configure method only provides a reference to the IServiceCollection so I am not able to call app.UseAuthorization.

Is there something I am missing or another way around this?

Thanks!

Error requesting access token for client default. Error = invalid_scope

I do not understand where this error is coming from. The App1 receives this message when executing a request towards App3

Error requesting access token for client default. Error = invalid_scope

The token server generated the following error:
IdentityServer4.Validation.TokenRequestValidator[0]
Client cannot request a refresh token in client credentials flow{ clientId = 709038.App1}, details: {
"ClientId": "709038.App1",
"ClientName": "App1",
"GrantType": "client_credentials",
"Scopes": "709038.App2.api 709038.App1.api address email multi-tenant.App3.api offline_access openid profile role",
"Raw": {
"grant_type": "client_credentials",
"client_id": "709038.App1",
"client_secret": "REDACTED"
}
}

The question is: why is it requesting offline_access and thus a refresh token? It indeed does not make sense to do so it seems. Same goes for address email and profile btw. Is there a way to configure the requested scopes specifically for when fetching a Client token?

I've simple configured it like this:
services.AddAccessTokenManagement()
.ConfigureBackchannelHttpClient()
.AddTransientHttpErrorPolicy(policy => policy.WaitAndRetryAsync(new[]
{
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(2),
TimeSpan.FromSeconds(3)
}));

        services.AddHttpClient(HttpClientNames.UserAccessDoNotRetry)
                .AddUserAccessTokenHandler();

        services.AddHttpClient(HttpClientNames.ClientAccessDoNotRetry)
                .AddClientAccessTokenHandler();

UserAccess works perfect

Is it possible to use authority address in AccessTokenManagement service.

Question.
When configuring AccessTokenManagementService it is necessary to provide token endpoint

services.AddAccessTokenManagement(o =>{

                o.Client.Clients.Add(new ClientCredentialsTokenRequest()
                {
                    Address = <TOKEN-END-POINT>
                });
            });

It could be more convenient to use authority address instead. Internally it could use IDiscoveryCache to retrieve token endpoint..

Idea is to minimize configuration of the application and use only one config item - authorityUrl.
All others items can be fetched from .well-know.

Otherwise need to provide authority - for DiscoveryService, tokenEndpoit for above, etc...

User.IsAuthenticated is false in OnValidatePrincipal

options.Events.OnValidatePrincipal = async e =>
{
    var currentToken = await e.HttpContext.GetUserAccessTokenAsync();

    if (string.IsNullOrWhiteSpace(currentToken))
    {
        e.RejectPrincipal();
    }
};

In this code snippet, calling GetUserAccessTokenAsync() always returns null because HttpContext.User.IsAuthenticated is false. However, e.Principal is fully populated with the proper principal/claims/etc.

Unable to get access token if multiple clients are configured where one of them is named "default"

Hi,

I have a setup where I have configured two clients:

services
                .AddTransient<ITokenClientConfigurationService, CustomTokenClientConfigurationService>()
                .AddAccessTokenManagement(options =>
            {
                var defaultRequest = new ClientCredentialsTokenRequest();
                Configuration.GetSection("TokenClient").Bind(defaultRequest);
                defaultRequest.Scope = "somescope";

                options.Client.Clients.Add(TokenClientNames.Default, defaultRequest);

                var someOtherRequest = defaultRequest.Clone<ClientCredentialsTokenRequest>();
                someOtherRequest.Scope = "someotherscope";

                options.Client.Clients.Add(TokenClientNames.SomeOther, someOtherRequest);
            });

This fails because the following lines returns false, causing the configuration to be looked up by GetOpenIdConnectSettingsAsync() which in turn requires the ConfigurationManager to be defined on the OpenId options:

if (_accessTokenManagementOptions.Client.Clients.Count == 1)

Could that line maybe instead be if(_accessTokenManagementOptions.Client.Clients.TryGetValue(clientName, out requestDetails)) and then return the requestDetails if found in the dictionary?

static ClientTokenRequestDictionary

Hello,

It is not a bug, just question ...

I not clearly understand why ClientTokenRequestDictionary defined as static?

public class AccessTokenManagementService : IAccessTokenManagementService {
    // ...
    static readonly ConcurrentDictionary<string, Lazy<Task<string>>> ClientTokenRequestDictionary =
            new ConcurrentDictionary<string, Lazy<Task<string>>>();
    // ...
    public async Task<string> GetClientAccessTokenAsync(string clientName = AccessTokenManagementDefaults.DefaultTokenClientName, bool forceRenewal = false) {
        // ...
        try
        {
            return await ClientTokenRequestDictionary.GetOrAdd(clientName, _ => { /* ... */ }).Value;
        }
        finally
        {
            ClientTokenRequestDictionary.TryRemove(clientName, out _);
        }
    }
}

Maybe it is OK .. but is possible case when it returns access token for other user?

Regards

Mike

Issue with Auth0 and refresh tokens

I'm getting the error "No refresh token found in cookie properties. A refresh token must be requested and SaveTokens must be enabled."

I've looked at the Auth0 docs and it appears they don't return the refresh_token upon refreshing the access token. You can see the response section here: https://auth0.com/docs/tokens/guides/use-refresh-tokens.

It looks like this could be fixed here:

await _userTokenStore.StoreTokenAsync(_httpContextAccessor.HttpContext.User, response.AccessToken, response.ExpiresIn, response.RefreshToken);

By changing:

await _userTokenStore.StoreTokenAsync(_httpContextAccessor.HttpContext.User, response.AccessToken, response.ExpiresIn, response.RefreshToken);

To:

await _userTokenStore.StoreTokenAsync(_httpContextAccessor.HttpContext.User, response.AccessToken, response.ExpiresIn, response.RefreshToken ?? userToken.RefreshToken);

If this is acceptable to you, I can submit a PR. If you'd prefer it be on some sort of flag, I can look into that too.

Also from the Auth0 docs (https://auth0.com/docs/tokens/concepts/refresh-tokens)

A Refresh Token is a special kind of token that can be used to obtain a renewed access token. You are able to request new access tokens until the Refresh Token is blacklisted. It’s important that refresh tokens are stored securely by the application because they essentially allow a user to remain authenticated forever.

System.FormatException: Input string was not in a correct format.

Hi mate,

im getting this and wondering if its coming from the caching layer of IdentityModel.

It happens after a couple of days of operation - maybe when the token expires?

Message: Input string was not in a correct format.
 ---> System.FormatException: Input string was not in a correct format.
at System.Number.StringToNumber(System.ReadOnlySpan`1 str, System.Globalization.NumberStyles options, System.NumberBuffer& number, System.Globalization.NumberFormatInfo info, System.Boolean parseDecimal) at offset 79
at System.Number.ParseInt64(System.ReadOnlySpan`1 value, System.Globalization.NumberStyles options, System.Globalization.NumberFormatInfo numfmt) at offset 22
at IdentityModel.AspNetCore.AccessTokenManagement.ClientAccessTokenCache.<GetAsync>d__4.MoveNext() at offset 245
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at offset 12
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task task) at offset 46
at IdentityModel.AspNetCore.AccessTokenManagement.AccessTokenManagementService.<GetClientAccessTokenAsync>d__10.MoveNext() at offset 183
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at offset 12
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task task) at offset 46
at IdentityModel.AspNetCore.AccessTokenManagement.ClientAccessTokenHandler.<SetTokenAsync>d__4.MoveNext() at offset 115
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at offset 12
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task task) at offset 46
at IdentityModel.AspNetCore.AccessTokenManagement.ClientAccessTokenHandler.<SendAsync>d__3.MoveNext() at offset 137
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at offset 12
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task task) at offset 46
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() at offset 11
at Microsoft.Extensions.Http.Logging.LoggingScopeHttpMessageHandler.<SendAsync>d__2.MoveNext() at offset 190
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at offset 12
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task task) at offset 46
at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult() at offset 11
at System.Net.Http.HttpClient.<FinishSendAsyncBuffered>d__62.MoveNext() at offset 141
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at offset 12
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task task) at offset 46

Using .Net core 2.2.
Im running ihostedservice tasks that all fail at the same time (they all use this client (from clientfactory)).

UserAccessTokenHandler requires refresh token

Using AddUserAccessTokenHandler (1) adds handling of refresh token and (2) sets the access token to the http request headers when using a http client .

Refresh token becomes a thing when requesting scope offline_access. If that scope is not requested refresh is not possible.
Currently AuthenticationSessionUserTokenStore.GetTokenAsync throws an exception if no refresh token is present. (No refresh token found in cookie properties. A refresh token must be requested and SaveTokens must be enabled)

Would it not make sense to make refresh token handling optional? So in case that no offline_access is requested handling refresh is not required but the access token is still set to the http request header.
That would have the positive effect that http client registration code is independent on the specific scopes used.

ClientAccessTokenHandler doesn't work outside of http requests

The ClientAccessTokenHandler is trying to get the IAccessTokenManagementService from the HttpContext, which is not available outside of an http request.

I would expect it to be getting this from it's constructor instead, as there shouldn't be anything that varies between requests, unlike the UserTokenHandler.

Support for PasswordTokenRequest or any TokenRequest

For some cases, client_credentials flow is not enough.
Maybe, for Worker Applications, in the same way as you support ClientCredentialsTokenRequest, it should be cool to handle PasswordTokenRequest as well?

Maybe to be more generic, the Clients dictionary in AccessTokenManagementOptions could be a IDictionary<string, T> where T is a any TokenRequest?

Add IServiceProvider in .AddAccessTokenManagement()

NOT an issue. Ignore it.

Current implementation
public static TokenManagementBuilder AddAccessTokenManagement(this IServiceCollection services, Action<AccessTokenManagementOptions> options = null)
Suggestion.

Can it be changed/added another overload with this signature:
public static TokenManagementBuilder AddAccessTokenManagement(this IServiceCollection services, Action<IServiceProvider, AccessTokenManagementOptions> options = null)

Reason:
I need to provide ClientAssertion value builder and would like to have there DI created object.

services.AddAccessTokenManagement(o =>
           {
               o.Client.Clients.Add("tokenClient_privateKeyJwt", new ClientCredentialsTokenRequest()
               {
                   Address = "tokenEndpoint",
                   ClientAssertion = new ClientAssertion()
                   {
                       Type = OidcConstants.ClientAssertionTypes.JwtBearer,
                       Value= TokenGenerator.GetToken() //static method. not good.
// Instead would be nice to have serviceProvider.GetRequiredService<TokenBuilder>.GetToken()
                   }
               });
           });

At this moment I use var tokenGenerator= services.BuildServiceProvider().GetRequiredService<TokenBuilder>();
but probably if Service provider given, code would be cleaner.. Something like

public static IHttpClientBuilder AddHttpClient<TClient>(this IServiceCollection services, Action<IServiceProvider, HttpClient> configureClient)

Any idea on the timing of a first stable release?

This library perfectly fits our needs for creating http clients with built-in token management. We have tried a poc implementing this package and are very excited about the results.

Our company policy however requires us to only use stable releases of packages.

Is there a rough timing available on when the first stable release can be expected?

Dependencies to HttpContext prevent use of this library in Blazor server side apps

Hi there,

i was looking for a way to refresh access tokens in a Blazor server side apps. The code in this library fits perfectly except the dependencies to HttpContext which is not available in Blazor server side.

My idea is to remove these dependencies where possible and make it compatible with the aforementioned scenario. The AccessTokenManagementService can then be used in a RevalidatingServerAuthenticationStateProvider to refresh the access token (and the id token along the way) periodically like

public class BlazorServerAuthenticationStateProvider : RevalidatingServerAuthenticationStateProvider
{
    protected override Task<bool> ValidateAuthenticationStateAsync(AuthenticationState authenticationState, CancellationToken cancellationToken)
    {
        ...

        // gets and refreshes access token based on the "expires_at" claim when necessary,
        // id token is refreshed in the process as original authorize request included the open_id scope
        var accessToken = _accessTokenManagementService.GetUserAccessTokenAsync(user);

I created PR #120 that includes the necessary changes.

Now one can simply add the following code to a Blazor server side app to refresh the access token right before a HTTP request is made via UserAccessTokenHandler:

Startup.cs

        // add custom user token store to avoid that AddAccessTokenManagement() registers its
        // own store based on HttpContext (HttpContext is not available in blazor server side)
        services.AddTransient<IUserTokenStore, UserTokenStore>();

        var tokenBuilder = services.AddAccessTokenManagement(options =>
        {
            options.User.Scheme = AzureADB2CDefaults.OpenIdScheme;
            options.User.RefreshBeforeExpiration = TimeSpan.FromMinutes(60).Subtract(TimeSpan.FromSeconds(30));
        });

        tokenBuilder.ConfigureBackchannelHttpClient();

       ...

        services
            // add a shared HttpClient ...
            .AddHttpClient("MyHttpClient", (serviceProvider, httpClient) =>
            {
               httpClient.BaseAddress = ...
            })
            // ... that is injected into the following typed clients ...
            .AddTypedClient<...>()
            ...
            // ... supported by a handler that adds the access token to requests
            .AddUserAccessTokenHandler();

Multi-tenancy considerations

Trying to think through how to implement my scenario and wanted to get some feedback before even attempting a PR

My IS4 instance is multitenant enabled via endpoint routing with tenant name in the path.

I have an Azure Function that needs to get a client_credentials token with a dynamic tenant. It's processing messages with a tenant name and needs to get the appropriate credentials.

So, right out of the gate, I can't use the DefaultTokenClientConfigurationService. Easy enough. But the issue remains I need to somehow have the context of which tenant I am connecting to.

I think if args were added to ITokenClientConfigurationService.GetClientCredentialsRequestAsync to pass in additional context that would solve my immediate need? Is that something that would be approved?

I could just parse out tenant names by passing it in with the client name, i.e. {tenantName}_{clientName} then split those in the custom ITokenClientConfigurationService, but I could see the need for wanting more stuff in a dynamic configuration like this.

In my instance for UserAccessTokens I can retrieve the multi tenant context via HttpContext, so that is covered, it's just client credentials that I need to address at the moment.

Anyways, looking for feedback :)

Exception "Headers are read-only, response has already started" when refreshing access token

Whenever the access token is refreshed, I get an exception "Headers are read-only, response has already started"

There is nothing special about my configuration:

services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = "Auth0";
})
.AddCookie(options =>
{
    // ...
})
.AddOpenIdConnect("Auth0", options =>
{
    // ...
});
services.AddAccessTokenManagement();
services.AddUserAccessTokenClient("MyApiClient", httpClient =>
{
    var uri = Configuration.GetValue<string>("MyApi:Url");
    httpClient.BaseAddress = new Uri(uri);
});

Here is there relevant parts of the stacktrace

 Error: System.InvalidOperationException: Headers are read-only, response has already started.
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpHeaders.ThrowHeadersReadOnlyException()
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpHeaders.Microsoft.AspNetCore.Http.IHeaderDictionary.set_Item(String key, StringValues value)
   at Microsoft.AspNetCore.Http.ResponseCookies.Append(String key, String value, CookieOptions options)
   at Microsoft.AspNetCore.Authentication.Cookies.ChunkingCookieManager.AppendResponseCookie(HttpContext context, String key, String value, CookieOptions options)
   at Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler.HandleSignInAsync(ClaimsPrincipal user, AuthenticationProperties properties)
   at Microsoft.AspNetCore.Authentication.AuthenticationService.SignInAsync(HttpContext context, String scheme, ClaimsPrincipal principal, AuthenticationProperties properties)
   at IdentityModel.AspNetCore.AccessTokenManagement.AuthenticationSessionUserTokenStore.StoreTokenAsync(ClaimsPrincipal user, String accessToken, Int32 expiresIn, String refreshToken)
   at IdentityModel.AspNetCore.AccessTokenManagement.AccessTokenManagementService.RefreshUserAccessTokenAsync()
   at IdentityModel.AspNetCore.AccessTokenManagement.AccessTokenManagementService.<GetUserAccessTokenAsync>b__12_1()
   at IdentityModel.AspNetCore.AccessTokenManagement.AccessTokenManagementService.GetUserAccessTokenAsync(Boolean forceRenewal)
   at Microsoft.AspNetCore.Authentication.TokenManagementHttpContextExtensions.GetUserAccessTokenAsync(HttpContext context, Boolean forceRenewal)
   at IdentityModel.AspNetCore.AccessTokenManagement.UserAccessTokenHandler.SetTokenAsync(HttpRequestMessage request, Boolean forceRenewal)
   at IdentityModel.AspNetCore.AccessTokenManagement.UserAccessTokenHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at Microsoft.Extensions.Http.Logging.LoggingScopeHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at System.Net.Http.HttpClient.FinishSendAsyncBuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)

cookie authentication security issue

If i run the application which in this repository.

I created one user in the application

i login in application all works

i open different browser and open application url

and then in that browser if i copy past the old browser cookies
after that refresh
user getting access to application without login by just copying the cookies of other user

Any way you can bump netstandard2.0;netcoreapp3.0 to netstandard2.1;netcoreapp3.1 and update your nugets?

Microsoft.AspNetCore.Authentication 2.1.0 is preventing me from using netcoreapp3.1 in my web app.

I am building a client library around IdentityModel.AspNetCore and when I nuget Microsoft.Extensions.DependencyInjection.Abstractions 3.1.2 everything blows up.

Assembly 'MyLib' with identity 'MyLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' uses 'Microsoft.Extensions.DependencyInjection.Abstractions, Version=3.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60' which has a higher version than referenced assembly 'Microsoft.Extensions.DependencyInjection.Abstractions' with identity 'Microsoft.Extensions.DependencyInjection.Abstractions, Version=3.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'

I believe this is because:

IdentityModel.AspNetCore/1.0.0-rc.3 -> Microsoft.AspNetCore.Authentication/2.1.0 -> Microsoft.AspNetCore.Authentication.Abstractions/2.1.0

ConfigureBackchannelHttpClient fails

In ASP.NET Core 3.0 if you use

services.AddTokenManagement()
    .ConfigureBackchannelHttpClient(client =>
    {
        client.Timeout = TimeSpan.FromSeconds(30);
    });

it fails with

System.InvalidOperationException: 
'The HttpClient factory already has a registered client with the type 'IdentityModel.AspNetCore.TokenEndpointService'. 
Client types must be unique. 
Consider using inheritance to create multiple unique types with the same API surface.'

Because both AddTokenManagement


and ConfigureBackchannelHttpClient
return Services.AddHttpClient<TokenEndpointService>(configureClient);

Register services.AddHttpClient<TokenEndpointService>

Custom grant_type

If during registartation in DI user sets GrantType to custom value like:

            options.Client.Clients.Add(TokenClientName, new ClientCredentialsTokenRequest
            {
                 Address = "https://demo.identityserver.io/connect/token",
                 ClientId = "m2m.short",
                 ClientSecret = "secret",
                 Scope = "api" // optional
                 GrantType = "custom_grant",
            });

and uses extension method AddClientAccessTokenHandler GrantType is overwritten by client_credentials value.

return await _httpClient.RequestClientCredentialsTokenAsync(requestDetails, cancellationToken: cancellationToken);

if in line above would be used extension method RequestTokenAsync instead of RequestClientCredentialsTokenAsync value set by user is preserved.

Is such behaviour be design?

As workaround it is possible to change implementation of ITokenEndpointService to copy of TokenEndpointService
and replace line mentioned above with something like

       var grantType = string.IsNullOrEmpty(requestDetails.GrantType)
           ? OidcConstants.GrantTypes.ClientCredentials
           : requestDetails.GrantType;

       return grantType == OidcConstants.GrantTypes.ClientCredentials
           ? await _httpClient.RequestClientCredentialsTokenAsync(requestDetails, cancellationToken: cancellationToken)
           : await _httpClient.RequestTokenAsync(requestDetails, cancellationToken: cancellationToken);

Proper refresh token approach

In version 4.0 of IdentityModel RefreshTokenDelegatingHandler and AccessTokenDelegatingHandler was removed with comment in release notes “I never was happy with their design, and they also did not work for some common scenarios”.
As I understand right this repo now contains ASP.NET part of the project so I have a question what is the problem with that implementation and what is suggested approach to do that in ASP.NET Core with IdentityModel? I can se ClientAccessTokenHandler but token is retrieved every request (no cache) and I’m wonder about performance.

Userinfo claims update

That would be nice to have option to update user claims (as an example via request to connect/userinfo) when we update access_token (via AddAccessTokenManagement). Or option to update idToken also (because it can have user claims too).

Default behavior for invalid/expired tokens

What is a default behavior when we got a HTTP response with 401 status from a resource?

The previous version (AccessTokenDelegatingHandler) tried to renew a token first and do an additional HTTP request to a resource.

But the current version - just sets a token from the store and never expire it (only checks exp claim)

Partially public API might end in System.ArgumentOutOfRangeException

Hey there, I'm using this fine preview library in conjunction with ProxyKit to automatically manage the access token for requests to the backend.

The backend itself has mutliple endpoints, some of them beeing callable as anonymous.

Startup isn't really interesting, but for completeness:

services.AddAccessTokenManagement();
services.AddProxy(httpClientBuilder => {
        httpClientBuilder.AddUserAccessTokenHandler();
});

Using the proxy will currently fail with a Exception:

System.ArgumentOutOfRangeException: The added or subtracted value results in an un-representable DateTime.
Parameter name: value
at System.DateTime.Subtract(TimeSpan value)
at System.DateTimeOffset.Subtract(TimeSpan value)
at IdentityModel.AspNetCore.AccessTokenManagement.AccessTokenManagementService.GetUserAccessTokenAsync() in C:\local\identity\model\IdentityModel.AspNetCore\src\AccessTokenManagement\AccessTokenManagementService.cs:line 110
at Microsoft.AspNetCore.Authentication.TokenManagementHttpContextExtensions.GetUserAccessTokenAsync(HttpContext context) in C:\local\identity\model\IdentityModel.AspNetCore\src\AccessTokenManagement\TokenManagementHttpContextExtensions.cs:line 26
at IdentityModel.AspNetCore.AccessTokenManagement.UserAccessTokenHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) in C:\local\identity\model\IdentityModel.AspNetCore\src\AccessTokenManagement\UserAccessTokenHandler.cs:line 32
at [...]

This originates from here


Where in the case of an unauthenticated user, the UserToken is an empty one.
Which will fail here:
var dtRefresh = userToken.Expiration.Subtract(_options.User.RefreshBeforeExpiration);

Exception is thrown when no tokens are found

I have multiple authentication schemes set up, one for OpenID Connect and then a custom one.

services
    .AddAuthentication(o => o.DefaultAuthenticateScheme = "customers-cookie")
    .AddCookie("customers-cookie", o =>
    {
        o.Cookie.Name = "login.customers";
    })
    .AddCookie("oidc-cookie", o =>
    {
        o.Cookie.Name = "login.admins";
    })
    .AddOpenIdConnect("oidc", o => o.SignInScheme = "oidc-cookie");

services.AddAccessTokenManagement();

services.AddClientAccessTokenClient("my-client");

The customers scheme is used by a custom sign-in handler.

When users are authenticated with the oidc-cookie scheme my-client works perfectly fine, a bearer token is appended to requests. The client also works when users are not authenticated, no bearer token is appended to requests. But when users are authenticated with the scheme customers an exception is thrown: No tokens found in cookie properties. SaveTokens must be enabled for automatic token refresh.. Which is true, there are no tokens in that cookie.

Is it possible to configure so my-client still works but no bearer token is appended when users are authenticated but there are no tokens? I.e. work the same as when users are not authenticated at all, or why does the code assume that there must be tokens when users are authenticated https://github.com/IdentityModel/IdentityModel.AspNetCore/blob/main/src/AccessTokenManagement/AuthenticationSessionUserTokenStore.cs#L46?

What is the logic when the oauth2 does only return access_token but no refresh_token?

When calling https://login.microsoftonline.com/{tenant-id}/oauth2/token, I get:

{
    "token_type": "Bearer",
    "expires_in": "3599",
    "ext_expires_in": "3599",
    "expires_on": "1595630297",
    "not_before": "1595626397",
    "resource": "https://xxx.azurewebsites.net",
    "access_token": "ey..."
}

And when I use the demo code from https://identitymodel.readthedocs.io/en/latest/aspnetcore/worker.html , all is working ok.

However, what will happen if 1 hour (> 3599 seconds) is passed, and I call the API again, will IdentityModel detect that a get or post to the API fails, and do a new call to aad to get a new access-token?

ID Token Refresh Implementation possible?

Hi there,

i have the requirement to implement an ID token refresh. The ID token is automatically refreshed alongside the access token refresh call but the new token returned is not processed hence the refreshed token gets lost. Changing code to process the refreshed token would surely result in a major rewrite of some parts. Do you accept changes to your repo in that direction? I would accept the challenge then 😎

So lonG
Daniel

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.