public async Task <RedirectResult> ConnectToActionstepCallback(string code, string returnUrl = null) { // We must use the API ClientId and Secret as these credentials will // be used to call the Actionstep API. var(successResult, receivedAt, tokenResponseJObject) = await ExchangeCodeAsync( $"{_actionstepService.TokenUri}", code, CreateActionstepCallbackUrl(returnUrl), _actionstepSettings.ApiClientId, _actionstepSettings.ApiClientSecret); if (!successResult) { // TODO throw new NotImplementedException("Not sure how to handle invalid response from OAuth2 token endpoint"); } var tokenSet = new TokenSet(tokenResponseJObject, receivedAt); if (tokenSet.IdToken == null) { throw new Exception("Unreadable API ID token response received from Actionstep."); } IdentityResult identityResult; var user = await _userManager.GetUserAsync(User); if (user == null) { var claims = tokenSet.IdToken.Claims; var email = claims.SingleOrDefault(c => c.Type == "email").Value; user = await _userManager.FindByEmailAsync(email); if (user == null) { user = new WCAUser { UserName = email, Email = email, FirstName = claims.SingleOrDefault(c => c.Type == "given_name").Value, LastName = claims.SingleOrDefault(c => c.Type == "family_name").Value, }; identityResult = await _userManager.CreateAsync(user); } await _signInManager.SignInAsync(user, false); } tokenSet.UserId = user.Id; await _mediator.Send(new AddOrUpdateActionstepCredentialCommand(tokenSet, user)); return(new RedirectResult( string.IsNullOrEmpty(returnUrl) ? "/wca/integrations" : returnUrl)); }