Retrieve userId and send private message with linqtotwitter - asp.net-mvc-3

I want to send private message to Twitter with my web app in asp.net mvc3. I was doing the authorization with oauth, here is my code:
in AccountController:
public ActionResult LogOn()
{
credentials.ConsumerKey = TwitterClient.ConsumerKey;
credentials.ConsumerSecret = TwitterClient.ConsumerSecret;
auth = new MvcAuthorizer { Credentials = credentials };
auth.CompleteAuthorization(Request.Url);
if (!auth.IsAuthorized)
{
string callbackUrl = "http://127.0.0.1:31891/Account/CompleteAuth";
Uri uri = new Uri(callbackUrl);
return auth.BeginAuthorization(uri);
}
return RedirectToAction("Index", "Home");
}
After the user is authorized , he is redirect to action CompleteAuth, and I get the token and tokenSecret, here is code of this action:
public ActionResult CompleteAuth(string oauth_token, string oauth_verifier)
{
string AccessToken = oauth_token;
string AccessTokenSecret = oauth_verifier;
TempData["AccessToken"] = AccessToken;
TempData["TokenSecret"] = AccessTokenSecret;
return RedirectToAction("Tweeting","Home");
}
after, it's redirect to Home in action Tweeting, when i try to get userId and send to him direct message :
public ActionResult Tweeting()
{
var auth = new MvcAuthorizer
{
Credentials = new InMemoryCredentials()
{
ConsumerKey = TwitterClient.ConsumerKey,
ConsumerSecret = TwitterClient.ConsumerSecret,
//OAuthToken = (string)Session["AccessToken"],
//AccessToken = Session["TokenSecret"]
OAuthToken = TempData["AccessToken"] as string,
AccessToken = TempData["TokenSecret"] as string
}
};
var twitterContext = new TwitterContext(auth);
var message = twitterContext.NewDirectMessage(auth.UserId, "Hi ucef, cool to discuss with you" + DateTime.Now);
return View();
}
But exception occurred because auth.UserId is null, have you any idea?

This appears to be the same as your previous question:
Send Private message with LinqToTwitter
Joe

Related

google calendar api net api The access token has expired and could not be refreshed. Errors: refresh error, refresh error, refresh error

I'm new to google api. I'm using google .net api, I'm getting the error:
The access token has expired and could not be refreshed. Errors: refresh error, refresh error, refresh error
I'm able to see the login screen, I accept the permissions request, and I got redirected to Index 2 correctly. When the calendar service is going to be used (execute) it crashes with the already mentioned error.
I already set the token as offline in the GOOGLE_AUTH_URL_TEMPLATE variable. That is supposed to be used for long term tokens.
Main Auth Class
public class TestOAuth2
{
private string applicationName = "g calendar test api";
private string clientId = "---";//From Google Developer console https://console.developers.google.com
private string clientSecret = "---";//From Google Developer console https://console.developers.google.com
private string[] scopes = new string[] {
CalendarService.Scope.Calendar, // Manage your calendars
CalendarService.Scope.CalendarReadonly // View your Calendars
};
private UserCredential userCredential;
string GOOGLE_AUTH_URL_TEMPLATE = "https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&approval_prompt=force&scope={0}&response_type=code&redirect_uri={1}&client_id={2}&login_hint=";
string clientRedirectUri= "http://localhost:57618/Home/Index2";
private CalendarService calendarService;
public TestOAuth2(string access_token)
{
ClientSecrets clientSecrets = new ClientSecrets
{
ClientId = clientId,
ClientSecret = clientSecret
};
GoogleAuthorizationCodeFlow flow = null;
TokenResponse token = new TokenResponse
{
AccessToken = access_token
};
flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = clientSecrets,
Scopes = scopes
});
userCredential = new UserCredential(flow, "user", token);
calendarService = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = userCredential,
ApplicationName = applicationName
});
}
public string GetGoogleOAuthURL()
{
return string.Format(GOOGLE_AUTH_URL_TEMPLATE, String.Join("+", scopes), clientRedirectUri, clientId);
}
private void InitializeCalendarService()
{
calendarService = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = userCredential,
ApplicationName = applicationName
});
}
public string GetEvents()
{
try
{
if (calendarService == null)
{
InitializeCalendarService();
}
var eventRequest = calendarService.Events.List("primary");
eventRequest.TimeMin = DateTime.Now;
eventRequest.ShowDeleted = false;
eventRequest.SingleEvents = true;
eventRequest.MaxResults = 10;
eventRequest.OrderBy = EventsResource.ListRequest.OrderByEnum.StartTime;
var events = eventRequest.Execute();
string sEvents = "";
foreach (var evt in events.Items)
{
sEvents += evt.Description + "\n<br>";
}
return sEvents;
}
catch (Exception ex)
{
return ex.Message;
}
}
}
Home Controller
public class HomeController : Controller
{
private TestOAuth2 Google;
public string GetGoogleOauthUrl()
{
string temp = "";
try
{
Google = new TestOAuth2(string.Empty);
temp = Google.GetGoogleOAuthURL();
}
catch (Exception ex)
{
temp = ex.Message;
}
return temp;
}
public string GetEvents()
{
string access_token = Request.Headers.Get("access_token");
string temp = "";
try
{
Google = new TestOAuth2(access_token);
temp = Google.GetEvents();
}
catch (Exception ex)
{
temp = ex.Message;
}
return temp;
}
public ActionResult Index()
{
return View();
}
public ActionResult Index2()
{
return View();
}
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
return View();
}
}
Index View
<html>
<body>
<script>
document.addEventListener('DOMContentLoaded', function (event) {
fetch("http://localhost:57618/Home/GetGoogleOauthUrl", {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
})
.then(response => {
if (response.ok) {
response.text().then(myAnswer => {
console.log(myAnswer);
window.open(myAnswer, '_blank');
})
} else {
console.error.log("HTTP-Error: " + response.status);
}
});
});
</script>
Hello! Wait For A New Window!
</body>
</html>
Index 2 View
<html>
<body>
<script>
function getUrlParameter(name) {
name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
var regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
var results = regex.exec(location.search);
return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
};
document.addEventListener('DOMContentLoaded', function (event) {
let token = getUrlParameter("code");
fetch("http://localhost:57618/Home/GetEvents", {
method: 'GET',
headers: {
'Content-Type': 'application/json',
"access_token": token
}
})
.then(response => {
if (response.ok) {
response.text().then(myAnswer => {
console.log(myAnswer);
document.write("<br><br><br>");
document.write(myAnswer);
})
} else {
console.error.log("HTTP-Error: " + response.status);
}
});
});
</script>
</body>
</html>
What you want to use is a special token that does not need to be refreshed I believe.
To do that you need to create a project inside GCP and then create a Service Account and Download a json key inside the new Service Account created.
Enable the Calendar API inside the new created project and copy the email of the service account. Go to the calendar you want to give access to your api and add the email you copy from the service account. Now you can use the service account json key to authenticate and it wont have expiration.

How to covert Json result into string in Blazor WebAssembly?

I want to convert the result into a string and pass it to the navigation path, but I couldn't do it, please help me.
HttpGet Controller
[HttpGet]
[Route("UserId")]
public async Task<ActionResult<ApplicationUser>> GetUserId(string Username)
{
var user = await userManager.FindByNameAsync(Username);
if (user == null)
return StatusCode(StatusCodes.Status500InternalServerError, new Response { Status = "Error", Message = "User not exist" });
var result = await userManager.GetUserIdAsync(user);
return new JsonResult(result);
}
Controller return result
"85e39a3e-8101-4166-9193-5e41bec1a7ce"
Function
private async Task Login()
{
var user = new userName { Username = Username };
var loginUser = new LoginDb { Username = Username, Password = Password };
if (Username == null || Password == null)
{
toastService.ShowWarning("Please enter Username and Password");
}
else
{
user = await Http.GetFromJsonAsync<userName>("Authentication/UserId?Username=" + Username);
if (user != null)
{
string Id = System.Text.Json.JsonSerializer.Serialize(user);
var result = await Http.PostAsJsonAsync("Authentication/login", loginUser);
if (result.IsSuccessStatusCode)
{
NavigationManager.NavigateTo("/profile/" + Id);
toastService.ShowSuccess("Login successful");
}
else
{
toastService.ShowError("Username or Password is wrong");
}
}
else
{
NavigationManager.NavigateTo("/login");
}
}
}
OK, I can see a few problems.
On the Server:
[HttpGet]
[Route("UserId")]
public async Task<ActionResult<ApplicationUser>> GetUserId(string Username) // A
{
var user = await userManager.FindByNameAsync(Username);
if (user == null) // B
return StatusCode(StatusCodes.Status500InternalServerError, new Response { Status = "Error", Message = "User not exist" });
var result = await userManager.GetUserIdAsync(user);
return new JsonResult(result);
}
First, your return type here is Task<ActionResult<ApplicationUser>> . ApplicationUser is tied to the backend Identity library, you can't and shouldn't use it for a DTO.
And you don't, in the end you have return new JsonResult(result); which is OK when you change the return type to just Task<ActionResult>.
On the client:
//user = await Http.GetFromJsonAsync<userName>("Authentication/UserId?Username=" + Username);
var userId = await Http.GetFromJsonAsync<string>("Authentication/UserId?Username=" + Username);
The endpoint returns a simple string. Json does not know about 'UserName' or anything else.
//string Id = System.Text.Json.JsonSerializer.Serialize(user); -- use UserId
You are serializing the Id (again) here, making it almost certainly invalid for an URL. So just skip that.

Set HTTPOnly cookie from the ASP.net Web Api

I am trying to implement jwt token authentication in my web api. I have a front end app which is in angular 8. i want to save the token in cookie.how can i achieve this?? what will be the syntax for the subsequent call from the angular,if i save the token in cookie after login.
TokenValidationHandler
internal class TokenValidationHandler : DelegatingHandler
{
private static bool TryRetrieveToken(HttpRequestMessage request, out string token)
{
token = null;
IEnumerable<string> authzHeaders;
if (!request.Headers.TryGetValues("Authorization", out authzHeaders) || authzHeaders.Count() > 1)
{
return false;
}
var bearerToken = authzHeaders.ElementAt(0);
token = bearerToken.StartsWith("Bearer ") ? bearerToken.Substring(7) : bearerToken;
return true;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
HttpStatusCode statusCode;
string token;
//determine whether a jwt exists or not
if (!TryRetrieveToken(request, out token))
{
statusCode = HttpStatusCode.Unauthorized;
//allow requests with no token - whether a action method needs an authentication can be set with the claimsauthorization attribute
return base.SendAsync(request, cancellationToken);
}
try
{
const string sec = "401b09eab3c013d4ca54922bb802bec8fd5318192b0a75f201d8b3727429090fb337591abd3e44453b954555b7a0812e1081c39b740293f765eae731f5a65ed1";
var now = DateTime.UtcNow;
var securityKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(sec));
SecurityToken securityToken;
JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
TokenValidationParameters validationParameters = new TokenValidationParameters()
{
ValidAudience = "http://localhost:50191",
ValidIssuer = "http://localhost:50191",
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
LifetimeValidator = this.LifetimeValidator,
IssuerSigningKey = securityKey
};
//extract and assign the user of the jwt
Thread.CurrentPrincipal = handler.ValidateToken(token, validationParameters, out securityToken);
HttpContext.Current.User = handler.ValidateToken(token, validationParameters, out securityToken);
return base.SendAsync(request, cancellationToken);
}
catch (SecurityTokenValidationException e)
{
statusCode = HttpStatusCode.Unauthorized;
}
catch (Exception ex)
{
statusCode = HttpStatusCode.InternalServerError;
}
return Task<HttpResponseMessage>.Factory.StartNew(() => new HttpResponseMessage(statusCode){ });
}
public bool LifetimeValidator(DateTime? notBefore, DateTime? expires, SecurityToken securityToken, TokenValidationParameters validationParameters)
{
if (expires != null)
{
if (DateTime.UtcNow < expires) return true;
}
return false;
}
}
LoginController(will generate the token)
public class LoginController : ApiController
{
[HttpPost]
public IHttpActionResult Authenticate([FromBody] LoginRequest login)
{
var loginResponse = new LoginResponse { };
LoginRequest loginrequest = new LoginRequest { };
loginrequest.Username = login.Username.ToLower();
loginrequest.Password = login.Password;
IHttpActionResult response;
HttpResponseMessage responseMsg = new HttpResponseMessage();
bool isUsernamePasswordValid = false;
if(login != null)
isUsernamePasswordValid=loginrequest.Password=="pass" ? true:false;
// if credentials are valid
if (isUsernamePasswordValid)
{
string token = createToken(loginrequest.Username);
//return the token
return Ok<string>(token);
}
else
{
// if credentials are not valid send unauthorized status code in response
loginResponse.responseMsg.StatusCode = HttpStatusCode.Unauthorized;
response = ResponseMessage(loginResponse.responseMsg);
return response;
}
}
private string createToken(string username)
{
//Set issued at date
DateTime issuedAt = DateTime.UtcNow;
//set the time when it expires
DateTime expires = DateTime.UtcNow.AddSeconds(30);
//http://stackoverflow.com/questions/18223868/how-to-encrypt-jwt-security-token
var tokenHandler = new JwtSecurityTokenHandler();
//create a identity and add claims to the user which we want to log in
ClaimsIdentity claimsIdentity = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.Name, username)
});
const string sec = "401b09eab3c013d4ca54922bb802bec8fd5318192b0a75f201d8b3727429090fb337591abd3e44453b954555b7a0812e1081c39b740293f765eae731f5a65ed1";
var now = DateTime.UtcNow;
var securityKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(sec));
var signingCredentials = new Microsoft.IdentityModel.Tokens.SigningCredentials(securityKey,Microsoft.IdentityModel.Tokens.SecurityAlgorithms.HmacSha256Signature);
//create the jwt
var token =
(JwtSecurityToken)
tokenHandler.CreateJwtSecurityToken(issuer:"http://localhost:50191",audience:"http://localhost:50191",
subject: claimsIdentity, notBefore: issuedAt, expires: expires, signingCredentials: signingCredentials);
var tokenString = tokenHandler.WriteToken(token);
return tokenString;
}
}
Firstly, the httponly cookie cannot be accessed from JavaScript.
Secondly, please refer to the code below to return the cookie along with the response.
private HttpResponseMessage setTokenCookie(LoginResponse response)
{
var responseMessage = Request.CreateResponse(HttpStatusCode.OK, response);
var cookie = new CookieHeaderValue("token", response.Token)
{
Expires = DateTimeOffset.Now.AddDays(7),
HttpOnly = true
};
responseMessage.Headers.AddCookies(new CookieHeaderValue[] { cookie });
return responseMessage;
}
You can use the function mentioned above in your controller action method as shown below to return the cookie.
var responseWithCookie = setTokenCookie(response);
return ResponseMessage(responseWithCookie);
I've assumed that LoginResponse class has Token property.

Response for preflight is invalid (redirect) using Asp.Net Web API

I'm developing a website which uses AngularJS in the frontend and ASP.NET web API in the backend. I'm trying to configure external logins(Facebook) for my site. I have already enabled Cors on the API. The order of API calls are:
API call to
api/Account/ExternalLogins?returnUrl=%2F&generateState=true
to get a list of external login providers.
This returns
[{"name":"Facebook",
"url":"/api/Account/ExternalLogin?provider=Facebook&response_type=token&client_id=self&redirect_uri=http%3A%2F%2Flocalhost%3A15359%2F&state=xxxxxxxxxx",
"state":"xxxxxxxxxx"}]
Now I send a GET request to url returned earlier. This triggers a preflight request to https://www.facebook.com/dialog/oauth?response_type=code&client_id=xxxxxxx&redirect_uri=https%3A%2F%2Flocalhost%3A44327%2Fsignin-facebook&scope=manage_pages&state=xxxxxxx which gives error. This is never redirected to Facebook Login Page.
Both the request and response headers for /api/Account/ExternalLogin?provider=Facebook&response_type=token&client_id=self&redirect_uri=http%3A%2F%2Flocalhost%3A15359%2F&state=xxxxxxxxxx contains Access-Control-Allow-Origin:*
But for the preflight request https://www.facebook.com/dialog/oauth?response_type=code&client_id=xxxxxxx&redirect_uri=https%3A%2F%2Flocalhost%3A44327%2Fsignin-facebook&scope=manage_pages&state=xxxxxxx Access-Control-Allow-Origin header is missing.
The code snippets are mentioned below.
app.js
$httpProvider.defaults.headers.common['Access-Control-Allow-Origin'] = '*';
$httpProvider.defaults.headers.common['Access-Control-Allow-Headers'] = 'Content-Type, Authorization, Content-Length, X-Requested-With';
$httpProvider.defaults.headers.common['Access-Control-Allow-Credentials'] = true;
$httpProvider.defaults.headers.common['Access-Control-Allow-Method'] = 'GET, PUT, POST, DELETE, OPTIONS';
Start.Auth.cs
// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
app.UseCors(CorsOptions.AllowAll);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
// Configure the sign in cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/api/Account/ExternalLogin"),
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
// In production mode set AllowInsecureHttp = false
AllowInsecureHttp = true
//Provider = new AuthorizationServerProvider()
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
// Enables the application to temporarily store user information when they are verifying the second factor in the two-factor authentication process.
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
// Enables the application to remember the second login verification factor such as phone or email.
// Once you check this option, your second step of verification during the login process will be remembered on the device where you logged in from.
// This is similar to the RememberMe option when you log in.
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
var facebookOptions = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions
{
AppId = "xxxxxxxxx",
AppSecret = "xxxxxxxxx",
BackchannelHttpHandler = new FacebookBackChannelHandler(),
Provider = new FacebookAuthenticationProvider()
{
OnAuthenticated = (context) =>
{
context.Identity.AddClaim(new System.Security.Claims.Claim("FacebookAccessToken", context.AccessToken));
return Task.FromResult(0);
}
},
SignInAsAuthenticationType = DefaultAuthenticationTypes.ExternalCookie,
SendAppSecretProof = true
};
facebookOptions.Scope.Add(ConfigurationManager.AppSettings["Facebook_Scope"]);
facebookOptions.AuthenticationMode = AuthenticationMode.Passive;
app.UseFacebookAuthentication(facebookOptions);
AccountController
[Authorize]
[EnableCors(origins: "*", headers: "*", methods: "*")]
[RoutePrefix("api/Account")]
public class AccountController : ApiController
{
private const string LocalLoginProvider = "Local";
private ApplicationUserManager _userManager;
public AccountController()
{
}
public AccountController(ApplicationUserManager userManager,
ISecureDataFormat<AuthenticationTicket> accessTokenFormat)
{
UserManager = userManager;
AccessTokenFormat = accessTokenFormat;
}
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? Request.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
public ISecureDataFormat<AuthenticationTicket> AccessTokenFormat { get; private set; }
// GET api/Account/UserInfo
[HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
[Route("UserInfo")]
public UserInfoViewModel GetUserInfo()
{
ExternalLoginData externalLogin = ExternalLoginData.FromIdentity(User.Identity as ClaimsIdentity);
return new UserInfoViewModel
{
Email = User.Identity.GetUserName(),
HasRegistered = externalLogin == null,
LoginProvider = externalLogin != null ? externalLogin.LoginProvider : null
};
}
// POST api/Account/Logout
[Route("Logout")]
[AllowAnonymous]
public IHttpActionResult Logout()
{
Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
return Ok();
}
// POST api/Account/AddExternalLogin
[Route("AddExternalLogin")]
public async Task<IHttpActionResult> AddExternalLogin(AddExternalLoginBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
AuthenticationTicket ticket = AccessTokenFormat.Unprotect(model.ExternalAccessToken);
if (ticket == null || ticket.Identity == null || (ticket.Properties != null
&& ticket.Properties.ExpiresUtc.HasValue
&& ticket.Properties.ExpiresUtc.Value < DateTimeOffset.UtcNow))
{
return BadRequest("External login failure.");
}
ExternalLoginData externalData = ExternalLoginData.FromIdentity(ticket.Identity);
if (externalData == null)
{
return BadRequest("The external login is already associated with an account.");
}
IdentityResult result = await UserManager.AddLoginAsync(User.Identity.GetUserId(),
new UserLoginInfo(externalData.LoginProvider, externalData.ProviderKey));
if (!result.Succeeded)
{
return GetErrorResult(result);
}
return Ok();
}
// GET api/Account/ExternalLogin
[OverrideAuthentication]
[HostAuthentication(DefaultAuthenticationTypes.ExternalCookie)]
[AllowAnonymous]
[Route("ExternalLogin", Name = "ExternalLogin")]
public async Task<IHttpActionResult> GetExternalLogin(string provider, string error = null)
{
if (error != null)
{
return Redirect(Url.Content("~/") + "#error=" + Uri.EscapeDataString(error));
}
if (!User.Identity.IsAuthenticated)
{
return new ChallengeResult(provider, this);
}
ExternalLoginData externalLogin = ExternalLoginData.FromIdentity(User.Identity as ClaimsIdentity);
if (externalLogin == null)
{
return InternalServerError();
}
if (externalLogin.LoginProvider != provider)
{
Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
return new ChallengeResult(provider, this);
}
ApplicationUser user = await UserManager.FindAsync(new UserLoginInfo(externalLogin.LoginProvider,
externalLogin.ProviderKey));
bool hasRegistered = user != null;
if (hasRegistered)
{
Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(UserManager,
OAuthDefaults.AuthenticationType);
ClaimsIdentity cookieIdentity = await user.GenerateUserIdentityAsync(UserManager,
CookieAuthenticationDefaults.AuthenticationType);
AuthenticationProperties properties = ApplicationOAuthProvider.CreateProperties(user.UserName);
Authentication.SignIn(properties, oAuthIdentity, cookieIdentity);
}
else
{
IEnumerable<Claim> claims = externalLogin.GetClaims();
ClaimsIdentity identity = new ClaimsIdentity(claims, OAuthDefaults.AuthenticationType);
Authentication.SignIn(identity);
}
return Ok();
}
// GET api/Account/ExternalLogins?returnUrl=%2F&generateState=true
[AllowAnonymous]
[Route("ExternalLogins")]
public IEnumerable<ExternalLoginViewModel> GetExternalLogins(string returnUrl, bool generateState = false)
{
IEnumerable<AuthenticationDescription> descriptions = Authentication.GetExternalAuthenticationTypes();
List<ExternalLoginViewModel> logins = new List<ExternalLoginViewModel>();
string state;
if (generateState)
{
const int strengthInBits = 256;
state = RandomOAuthStateGenerator.Generate(strengthInBits);
}
else
{
state = null;
}
foreach (AuthenticationDescription description in descriptions)
{
ExternalLoginViewModel login = new ExternalLoginViewModel
{
Name = description.Caption,
Url = Url.Route("ExternalLogin", new
{
provider = description.AuthenticationType,
response_type = "token",
client_id = Startup.PublicClientId,
redirect_uri = new Uri(Request.RequestUri, returnUrl).AbsoluteUri,
state = state
}),
State = state
};
logins.Add(login);
}
return logins;
}
// POST api/Account/Register
[AllowAnonymous]
[Route("Register")]
public async Task<IHttpActionResult> Register(RegisterBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var user = new ApplicationUser() { UserName = model.Email, Email = model.Email };
IdentityResult result = await UserManager.CreateAsync(user, model.Password);
if (!result.Succeeded)
{
return GetErrorResult(result);
}
return Ok();
}
// POST api/Account/RegisterExternal
[OverrideAuthentication]
[HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
[Route("RegisterExternal")]
public async Task<IHttpActionResult> RegisterExternal(RegisterExternalBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var info = await Authentication.GetExternalLoginInfoAsync();
if (info == null)
{
return InternalServerError();
}
var user = new ApplicationUser() { UserName = model.Email, Email = model.Email };
IdentityResult result = await UserManager.CreateAsync(user);
if (!result.Succeeded)
{
return GetErrorResult(result);
}
result = await UserManager.AddLoginAsync(user.Id, info.Login);
if (!result.Succeeded)
{
return GetErrorResult(result);
}
return Ok();
}
protected override void Dispose(bool disposing)
{
if (disposing && _userManager != null)
{
_userManager.Dispose();
_userManager = null;
}
base.Dispose(disposing);
}
}
I know this is achievable using MVC Account Controller. I'm looking for a way to do this entirely using web api with no dependency on MVC.
Thanks!

Getting 400 bad request when I use "ValidateAntiForgeryToken" and "Authorize"

I'm using asp.net core in the server side and xamarin in the client side.I use JWT token and I want to validate forgery token at the same time.
This is my client side code :
public async Task<string> PostAsync(object model, string url)
{
var cookieContainer = new CookieContainer();
var handlerhttps = new HttpClientHandler
{
UseCookies = true,
UseDefaultCredentials = true,
CookieContainer = cookieContainer
};
var clientPage = new HttpClient(handler: handlerhttps)
{
BaseAddress = new Uri(uriString: Application.Current.Resources[index: "Domain"] + "/api/token")
};
var pageWithToken = await clientPage.GetAsync(requestUri: clientPage.BaseAddress);
var verificationToken = await pageWithToken.Content.ReadAsStringAsync();
using (var handler = new HttpClientHandler
{
CookieContainer = cookieContainer,
UseDefaultCredentials = true,
UseCookies = true
})
{
using (var client = new HttpClient(handler: handler) {BaseAddress = new Uri(uriString: url)})
{
client.DefaultRequestHeaders.Add(name: "RequestVerificationToken", value: verificationToken);
if (Application.Current.Properties[key: "Token"] != null)
{
var token = Application.Current.Properties[key: "Token"].ToString();
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue(scheme: "Bearer", parameter: token);
}
var json = JsonConvert.SerializeObject(value: model);
var content = new StringContent(content: json, encoding: Encoding.UTF8,
mediaType: "application/json");
var response = await client.PostAsync(requestUri: client.BaseAddress, content: content);
var result = await response.Content.ReadAsStringAsync();
return result;
}
}
}
My problem is when I use [ValidateAntiForgeryToken] and [Authorize] in the server side at the same time, I get 400 bad request.
But when I remove [ValidateAntiForgeryToken], It will authorize without any problem.
When I remove [Authorize] , I don't get 400 bad request and it validate forgery token successfully.
I don't know how to solve this problem.
If you are using Microsoft.AspNetCore.Mvc.TagHelpers it will add a input field with a 'difficult to guess' code:
<input name="__RequestVerificationToken" type="hidden" value="CfDJ8PXv-VNSuRBLvOlUgHlQcf4p8B29vW6EKn4ENesSgHR79kWTvbnQ9a1Taj90b-e66-79H7Nx5ljHnvPbwqfSNqHMRMaBkoRKGsTxtbZZlq0rSl2zbGK2aKpPQc0qnoNuRehSNhP097P5-Vlp-3OSPYdIqLQJSqIsPDaQsEOXsNU4qIIDrj-tIhqk5EW9tTYc6g">
Anyways, even if you add #Html.AntiForgeryToken() it wouldn't conflict. However you cannot decorate the 'first' controller action with [ValidateAntiForgeryToken], only the final one with will get the POST.
Example:
Action 1
[HttpPost]
[AllowAnonymous]
[ActionName("SpecialSignUp")]
public IActionResult Index([FromForm] string email)
{
// TODO : code in here
return View(email);
}
Users will be redirect to above action through a POST.
Let's say that the view above displays a form with the email field pre-filled and other fields to be filled.
If you decorate it with [ValidateAntiForgeryToken] you will get a 400 (Bad Request). Removing it, all will be fine.
Action 2
[HttpPost]
[AllowAnonymous] // It could be [Authorized]
[ActionName("SpecialSignUp")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> LaunchSignUpAsync([FromForm] SpecialSignUpViewModel model)
{
// TODO : Code in here
return RedirectToAction("OtherActionX", data);
}
Form will be posted by the above View
Now all it's working, no conflict is happening anymore. If you respect that sequence, it will work for you!
I had the same issue, and it was happening because I decorated the 'Action 1' with [ValidateAntiForgeryToken] as well.
Hopefully it helps!
I faced a similar issue but got it resolved by adding "RequestVerificationToken" on my request
My controller code (sample)
HttpPost("SignOut")
Authorize ==> using JWT
ValidateAntiForgeryToken
*/
You can try making a custom method for validation of JWT token to replace the
[Authorize] by
public void ValidateJWT(string token, out bool status)
{
if (token == null)
{
status = false;
}
byte[] secretKey = System.Text.Encoding.UTF8.GetBytes("your jwt secret key");
var tokenHandler = new JwtSecurityTokenHandler();
try
{
tokenHandler.ValidateToken(token, new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(secretKey),
ValidateIssuer = false,
ValidateAudience = false,
ClockSkew = TimeSpan.Zero
}, out SecurityToken validatedToken);
status = true;
}
catch
{
status = false;
}
}

Resources