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

AspNetCore OAuth sample #61

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open

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