Skip to content

Conversation

@jaredpar
Copy link
Member

This provides an OAuth sample implementation for Asp.Net Core.

This is my first attempt at writing my own OAuth provider for Asp.Net
Core and I'm also fairly new to OAuth in general. So please let me know
if there is anything that can be improved in this sample

closes #45

This provides an OAuth sample implementation for Asp.Net Core.

This is my first attempt at writing my own OAuth provider for Asp.Net
Core and I'm also fairly new to OAuth in general. So please let me know
if there is anything that can be improved in this sample

closes microsoft#45
@andmig-ilty
Copy link

I tried the same on a Blazor Server application and it works pretty well. It would be usefull to have sample API calls with the obtained access_token

@pgmsft
Copy link

pgmsft commented Sep 7, 2020

It would be great if you add an example API call with access_token, and code to renew the access_token using refresh_token.

@pgmsft
Copy link

pgmsft commented Sep 20, 2020

In case this is useful: this is how one can renew access_token using refresh_token, if access_token is expired:

public void ConfigureServices(IServiceCollection services)
{
            <snip>

            AddCookie(options =>
            {
                options.LoginPath = "/signin";
                options.LogoutPath = "/signout";
                options.Events = new CookieAuthenticationEvents
                {
                    OnValidatePrincipal = async x =>
                    {
                        await AdoOAuthUtils.OnValidatePrincipal(x, Configuration);
                    }
                };
            });
}
        public static async Task OnValidatePrincipal(CookieValidatePrincipalContext context, IConfiguration configuration)
        {
            if (context.Principal.Identity.IsAuthenticated)
            {
                var tokens = context.Properties.GetTokens();
                var refreshToken = tokens.FirstOrDefault(t => t.Name == "refresh_token");
                var accessToken = tokens.FirstOrDefault(t => t.Name == "access_token");
                var expiresAt = tokens.FirstOrDefault(t => t.Name == "expires_at");
                var expireTime = DateTime.Parse(expiresAt.Value);
                var nowPlus5min = DateTime.Now + TimeSpan.FromMinutes(5);
                // If this token expires in next 5 mins, renew it.
                if (expireTime < nowPlus5min)
                {
                    var tokenModel = await RefreshToken(refreshToken.Value, configuration);
                    if (tokenModel.IsError)
                    {
                        context.RejectPrincipal();
                        return;
                    }
                    refreshToken.Value = tokenModel.RefreshToken;
                    accessToken.Value = tokenModel.AccessToken;
                    var newExpires = DateTime.UtcNow + TimeSpan.FromSeconds(tokenModel.ExpiresIn);
                    expiresAt.Value = newExpires.ToString("o", CultureInfo.InvariantCulture);
                    context.Properties.StoreTokens(tokens);
                    context.ShouldRenew = true;
                }
            }
        }
        private static async Task<TokenModel> RefreshToken(string refreshToken, IConfiguration configuration)
        {
            TokenModel tokenModel = new TokenModel();
            if (!String.IsNullOrEmpty(refreshToken))
            {
                HttpClient httpClient = new HttpClient();
                HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Post, AdoAuthenticationDefaults.TokenEndPoint);
                requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

                Dictionary<String, String> form = new Dictionary<String, String>()
                {
                    { "client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" },
                    { "client_assertion", configuration["AzureClientSecret"] },
                    { "grant_type", "refresh_token" },
                    { "assertion", refreshToken },
                    { "redirect_uri", "https://localhost:44352" + AdoAuthenticationDefaults.CallbackPath }
                };
                requestMessage.Content = new FormUrlEncodedContent(form);

                // Make the request to exchange the auth code for an access token (and refresh token)
                HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);

                if (responseMessage.IsSuccessStatusCode)
                {
                    // Handle successful request
                    String body = await responseMessage.Content.ReadAsStringAsync();
                    tokenModel = JObject.Parse(body).ToObject<TokenModel>();
                }
                else
                {
                    tokenModel.IsError = true;
                    tokenModel.ErrorMessage = responseMessage.ReasonPhrase;
                }
            }
            else
            {
                tokenModel.IsError = true;
                tokenModel.ErrorMessage = "Invalid refresh_token";
            }

            return tokenModel;
        }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

oAuth for DevOps from Asp.Net Core app (3.0)

3 participants