public async Task <IActionResult> LoginComplete([FromQuery] string code, [FromQuery] string state) { const string UserDataParam = "data"; const string SuccessParam = "successful"; const string ErrorParam = "error"; const string CodeParam = "code"; var stateDe = StateData.Decrypt(stateEncAlgo, state); if (!stateDe.IsValid) { // state invalid; refuse to brew coffee return(this.ImATeapot()); } var successCb = stateDe.SuccessCallback; if (stateDe.UserData != null) { successCb = QueryHelpers.AddQueryString(successCb, UserDataParam, stateDe.UserData); } var failureCb = stateDe.FailureCallback; if (failureCb != null && stateDe.UserData != null) { failureCb = QueryHelpers.AddQueryString(failureCb, UserDataParam, stateDe.UserData); } else if (failureCb == null) { failureCb = QueryHelpers.AddQueryString(successCb, SuccessParam, "false"); successCb = QueryHelpers.AddQueryString(successCb, SuccessParam, "true"); } OauthToken token; try { token = await client.Oauth.CreateAccessToken( new OauthTokenRequest(githubAuthSettings.ClientId, githubAuthSettings.ClientSecret, code) { RedirectUri = new Uri(Url.AbsoluteRouteUrl(LoginCallbackName)) }); } // will not catch NotFound, Validation, or LegalRestriction because those are hard errors catch (AuthorizationException e) { return(Redirect(QueryHelpers.AddQueryString(failureCb, ErrorParam, $"GitHub returned Unauthorized: {e.Message}"))); } catch (ForbiddenException e) { return(Redirect(QueryHelpers.AddQueryString(failureCb, ErrorParam, $"GitHub returned Forbidden: {e.Message}"))); } catch (ApiException e) { return(Redirect(QueryHelpers.AddQueryString(failureCb, ErrorParam, $"GitHub returned error: {e.Message}"))); } if (token.TokenType != "bearer") // don't know what to do with it { return(Redirect(QueryHelpers.AddQueryString(failureCb, ErrorParam, $"API returned unknown token type {token.TokenType}"))); } var newCode = Utils.GetCryptoRandomHexString(8); // keep it somewhat short while (await repoContext.AuthCodes.AnyAsync(s => s.Code == newCode)) // in the odd case that 2 exist at once { newCode = Utils.GetCryptoRandomHexString(8); } repoContext.AuthCodes.Add(new AuthCodeTempStore { Code = newCode, GitHubBearer = token.AccessToken }); await repoContext.SaveChangesAsync(); // these saves feel kinda gross ngl // TODO: will this ever cause a race condition? return(Redirect(QueryHelpers.AddQueryString(successCb, CodeParam, newCode))); }