How can i handle JWT Tokens in my application - c#

I'm a full time systems engineering student and i have a project that i've hit a bumped on the road, i'm handling right now all the users state with sessions and i would like to implement the JWT token for more "security", the application is a 2 part app (Api's = backend in one solution, Frontend in another solution)
Have to mention, I'm using C# in VS 2022 with asp.net .Net framework 4.7.2
The code architechture is MVC.
Here is my code
This is in the Users Model:
public string GetToken(Guid Id)
{
try
{
var key = ConfigurationManager.AppSettings["JwtKey"];
var issuer = ConfigurationManager.AppSettings["JwtIssuer"];
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
//Create a List of Claims, Keep claims name short
var permClaims = new List<Claim>();
permClaims.Add(new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()));
permClaims.Add(new Claim("userid", "Id"));
//Create Security Token object by giving required parameters
var token = new JwtSecurityToken(issuer, //Issure
issuer, //Audience
permClaims,
expires: DateTime.Now.AddDays(1),
signingCredentials: credentials);
var jwt_token = new JwtSecurityTokenHandler().WriteToken(token);
return jwt_token;
}
catch(Exception ex)
{
return string.Empty;
}
}
Once i get this token i include it in the Users object response and i'm able to see it in the front end response, i'm able to see the information being returned succesfully, I'm storing this data in a session variable.
this is my LogIn Controller:
[HttpPost]
public ActionResult ValidateUser(Users User)
{
if (User.noHashPass != null && User.Username != null)
{
var datos = model.ValidateUser(User);
var tok = datos.User;
if (datos != null)
{
Session["User"] = new Users();
Session["User"] = datos.User;
return RedirectToAction("Index", "Home");
}
else
{
return ViewBag.Msj = "¡ERROR! El usuario o la contraseña son incorrectos. Por favor intente de nuevo."; ;
}
}
else
{
ViewBag.Msj = "¡ERROR! El usuario o la contraseña son incorrectos. Por favor intente de nuevo.";
}
return View();
}
i was trying to do this to extract just the token piece in the users model:
public Respuesta ViewUserById(Guid Id)
{
using (var client = new HttpClient())
{
try
{
object sesion = new Users();
sesion = HttpContext.Current.Session["User"];
string api = "Users/ViewUserById?Id=" + Id;
string route = Url + api;
HttpResponseMessage response = client.GetAsync(route).Result;
response.EnsureSuccessStatusCode();
if (response.IsSuccessStatusCode)
{
var responseBody = response.Content.ReadAsAsync<Respuesta>().Result; //serializacion del obj JSON a un objeto
return responseBody;
}
else
{
throw new Exception("No se encontro un usuario existente bajo el Id:" + " " + Id);
}
}
catch (Exception ex)
{
throw ex;
}
}
}
But the sesion variable only gives me a ToString() function, i can see the sesion variable filling up and getting all the data but i cannot handle it for example, sesion.Token to get the Token inside the session variable.
Any suggestions are appreciated.
How can i handle the session variable in order to get the data within?

Related

Adding new field to an object

I am new to c# and .net api. I am doing a login method where if a user is found in database, it will return the user.The user object is likethis,
{ email: user#email.com, password: password, name: User Name, }
First thing, I want to remove the password from the return object, second I want to add the JWT token to the return object. Here is my code:
public object LoginCurrentUser(User user) {
var result = AuthenticateUser(user);
if (result != null)
{
var token = Generate(user);
//I want to create new variable here that removes the password and adds the token to the field.
return result;
}
else {
return null;
}
}
It should be pretty straight forward.
if (result != null)
{
var token = Generate(user);
var response = new {
Email = result.Email,
Name = result.Name,
Token = token
};
return response;
}
Alternatively, You can create a DTO class for the response and return its object.

Angular requests to ASP.NET return "Error Unknown" when under SSL/TLS certificate

I've walked through this trouble and I've still didn't found any answer:
My Angular application sends a bunch of HTTP requests to asp.net. Asp.net process the request ( token check, etc.. ) and returns the data as ordered.
Thing is, on IIS, I've added SSL/TLS certificate from Let's Encrypt and the application never worked again.
Strange behaviours I've notivced
Only login method works. All request that follow, return "Unknown Error - Status Code 0"
Asp.net has an interceptor that filters every request to check the token. If I send the Authorization header, the application fails. If I don't, asp.net returns 404 error stating that token doesnt exists on the database ( as it shoud )
In Postman, the requests works 100% ( even the check of authenticity of the token)
If I remove either the angular interceptor and the middleware in asp.net AND remove the SSL cert. the application works
Chrome throws the "net::ERR_SPDY_PROTOCOL_ERROR" exception.
I've been over my head for two days and found no answer. Looked up on StackOverflow, IIS foruns, Angular foruns, .Net foruns... Nothing
Here some code:
Angular Interceptor
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// Filtrar apenas pedidos feitos à API
if(req.url.match("\\api")) {
let newRequest = req.clone({
headers: new HttpHeaders({
'Content-Type':'application/json',
'Authorization':this.cookieService.get('at')
})
});
return next.handle(newRequest);
} else {
return next.handle(req);
}
}
Asp.NET Middleware
public Task Invoke(HttpContext context) {
// Check if request is redirected to ".../api/exemplo/xyx"
// To exclude the 'login' request
if (context.Request.Path.Value.StartsWith("/api")) {
// Check if request is NOT an OPTIONS
if(!context.Request.Method.Equals("OPTIONS")){
// Check for header
string auth = context.Request.Headers["Authorization"].ToString();
FbConnectionStringBuilder csb = new FbConnectionStringBuilder {
DataSource = _appSettings.DataSource,
Port = _appSettings.Port,
Database = _appSettings.Database,
UserID = _appSettings.UserID,
Password = _appSettings.Password,
ServerType = FbServerType.Default
};
// Open db
FbConnection db = new FbConnection(csb.ToString());
db.Open();
// Get the expiration date of token
string query = "SELECT DT_REVOG FROM ORGANIG_TOKEN WHERE TOKEN = #auth";
DateTime now = DateTime.UtcNow;
DateTime DateRevogToken;
try
{
DateRevogToken = db.QuerySingle<DateTime>(query, new { auth });
db.Close();
if (now >= DateRevogToken)
{
// Expired
string deleteQuery = "DELETE FROM ORGANIG_TOKEN WHERE TOKEN = #auth";
db.Execute(deleteQuery, new { auth });
context.Response.StatusCode = 401;
context.Response.WriteAsync("A token não é válida");
return Task.CompletedTask;
}
else
{
// Is valid
return _next(context);
}
} catch(Exception e) {
context.Response.StatusCode = 404;
context.Response.WriteAsync("Ocorreu um erro na verificação da token: " + e.Message);
return Task.CompletedTask;
}
}
else {
//It's a OPTIONS request. Ignore
return _next(context);
}
}
else {
// Login request. Ignore
return _next(context);
}
}
}
Login request (asp.net)
[Route("auth/gerar_token")]
[AllowAnonymous]
[HttpPost]
public async Task<IActionResult> GerarToken([FromBody] Client c)
{
// Conexão á base de dados Firebird
FbConnectionStringBuilder csb = new FbConnectionStringBuilder
{
DataSource = _appSettings.DataSource,
Port = _appSettings.Port,
Database = _appSettings.Database,
UserID = _appSettings.UserID,
Password = _appSettings.Password,
ServerType = FbServerType.Default
};
Client client = new Client();
string login = c.login;
string pass = c.pass;
string hashedPass = null;
string responseMessage;
// Hash da password do client
using (MD5 md5Hash = MD5.Create()) { hashedPass = GetMd5Hash(md5Hash, pass); }
using (var db = new FbConnection(csb.ToString())) {
string token;
try {
await db.OpenAsync();
// Query de login
string sql = #"SELECT CD, NOME, RAMO_ACT, DESIGN, EMAIL, LOGIN, MORAD
FROM ORGANIG WHERE LOGIN = #login AND PASS = #hashedPass";
client = await db.QuerySingleAsync<Client>(sql, new { login, hashedPass });
try {
int cd_organig = client.cd;
token = GenerateRefreshToken();
DateTime dt_cria = DateTime.UtcNow;
DateTime dt_revog = dt_cria.AddHours(4);
DateTime dt_inicio = DateTime.UtcNow;
string sql_token = #"INSERT INTO ORGANIG_TOKEN(TOKEN, CD_ORGANIG, DT_CRIA, DT_REVOG) VALUES (#token, #cd_organig, #dt_cria, #dt_revog)";
db.Execute(sql_token, new { token, cd_organig, dt_cria, dt_revog });
string sql_sessao = #"INSERT INTO ORGANIG_SESSAO(CD_ORGANIG, DT_INICIO, TOKEN, DT_REVOG_TOKEN) VALUES (#cd_organig, #dt_inicio, #token, #dt_revog)";
db.Execute(sql_sessao, new { cd_organig, dt_inicio, token, dt_revog });
}
catch (Exception e) {
JObject serverErroResponse = new JObject {
{ "Ocorreu um erro no servidor. Detalhes: ", e.Message }
};
db.Close();
return StatusCode(500, serverErroResponse);
}
JObject response = new JObject {
{ "token", token },
{ "cd", client.cd },
{ "nome", client.nome }
};
db.Close();
return Ok(response);
}
catch (Exception e) {
if(e.Message.Equals("Sequence contains no elements")) {
responseMessage = "Credenciais inválidas. Por favor, tente novamente";
} else {
responseMessage = "Ocorreu um erro no servidor. Detalhes: " + e.Message;
}
JObject serverErrorResponse = new JObject {
{ "message", responseMessage }
};
db.Close();
return StatusCode(500, serverErrorResponse);
}
} // using (var db = new FbConnection(csb.ToString()))
}
Example of request that doesnt work
[Route("api/build_organig/{cd}")]
[HttpGet]
public IActionResult BuildOrganig(int cd)
{
// Conexão á base de dados Firebird
FbConnectionStringBuilder csb = new FbConnectionStringBuilder {
DataSource = _appSettings.DataSource,
Port = _appSettings.Port,
Database = _appSettings.Database,
UserID = _appSettings.UserID,
Password = _appSettings.Password,
ServerType = FbServerType.Default
};
// Abrir conexão á base de dados
FbConnection db = new FbConnection(csb.ToString());
db.Open();
// Query para retornar os dados do procedure para uma lista
List<BuildOrganig> buildOrganigList = new List<BuildOrganig>();
buildOrganigList = db.Query<BuildOrganig>("SELECT CD, CD_BASE, NOME FROM BUILD_ORGANIG(#cd) ORDER BY NIVEL", new { cd }).ToList();
if (buildOrganigList.Count == 0) {
JObject emptyResponse = new JObject {
{ "count", buildOrganigList.Count },
{ "message", "Organigrama não existente" }
};
db.Close();
return StatusCode(404, emptyResponse);
}
db.Close();
return StatusCode(200, buildOrganigList);
}
Here are some screenshots of postman and the browser
If you need more info, just state in the comments.
Thanks
I had similar issues with Chrome and custom auth headers giving me a SPDY error.
I changed the name of my Authorization header to "X-Custom-Auth" and gave the OPTIONS a special response like this:
response.AddHeader("Access-Control-Allow-Methods", "GET, POST");
// Important: copy back the expected allowed headers
response.Headers["Access-Control-Allow-Headers"] = request.Headers["Access-Control-Request-Headers"];
response.AddHeader("Access-Control-Max-Age", "1728000");
response.Flush();
response.End();
You may also need to add your custom auth header name to web config or CORS config.
Hope it also applies in your case.

management problem of a NullReferenceException

I have an issue in the Try Catch block of my code below.
In function SetUser, I use the getId function that returns an Id if the user exists in DB otherwise, I get a NullReferenceException.
I call this function in the try catch block in Login. I have a problem with the catch because when the exception is generated, I would like the user to be redirected to the register page. But when I try to execute my code with a non-existing user, I think that I have a kind of infinite loop because my page doesn't stop loading. I don't understand what I'm doing wrong. Need help please
function Login:
public static void Login(HttpRequest Request, HttpResponse Response, string redirectUri)
{
if (Request.IsAuthenticated)
return;
if (!Request.Form.AllKeys.Contains("id_token"))
return;
string value = Request.Form.Get("id_token");
JObject id_token = JwtDecode(value);
string upn = id_token.GetValue("upn").ToString();
DateTime expiretime = GetExpireTime(id_token);
try
{
SetUser(id_token);
}
catch (Exception ex)
{
Response.Redirect("~/register.aspx");
}
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, upn, DateTime.UtcNow, expiretime, false, id_token.ToString(), FormsAuthentication.FormsCookiePath);
string encryptedcookie = FormsAuthentication.Encrypt(ticket);
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedcookie);
cookie.Expires = expiretime;
Response.Cookies.Add(cookie);
redirectUri = GetRedirectUrl(Request, redirectUri);
Response.Redirect(redirectUri, true);
}
function setUser:
private static void SetUser(JObject id_token)
{
string email = id_token.GetValue("unique_name").ToString();
string name = id_token.GetValue("given_name").ToString();
DataSet ds;
List<Claim> claims = new List<Claim>()
{
new Claim(ClaimTypes.Email, email),
new Claim(ClaimTypes.Name, GetId(email))
};
string roles= "SELECT name FROM AspNetRoles;
ds = GetDataSet(roles);
if (ds.Tables.Count > 0)
{
foreach (var row in ds.Tables(0).Rows)
claims.Add(new Claim(ClaimTypes.Role, row("name")));
}
ClaimsIdentity claimsIdentity = new ClaimsIdentity(claims, "Cookies");
ClaimsPrincipal principal = new ClaimsPrincipal(claimsIdentity);
HttpContext.Current.GetOwinContext().Authentication.User = principal;
Thread.CurrentPrincipal = principal;
}
function getId:
public static string getId(string email)
{
return ((new UserManager()).FindByEmail(email)).Id;
}

FormAuthentication .ASPXAUTH cookie showing null after few moments in ASP.NET MVC

I have implemented FormAuthentication in asp.net mvc 5 and create FormsAuthenticationticket on LogIn and it creates successfully but after few moments that cookie is showing in browser but in application it's getting null.
Please help to solve this issue.
Any help will be appreciated Thanks
LOGIN FORM
public ActionResult Login([Bind(Include = "Username, Password")] LoginModel loginModel, string ReturnUrl)
{
if (ModelState.IsValid)
{
Egov_Users eGov_Users = db.Egov_Users
.Where(p => p.UserType.Type != "O" && p.UserName == loginModel.Username)
.FirstOrDefault();
if (eGov_Users == null)
{
ModelState.AddModelError("", "Invalid username");
return View();
}
else
{
if (eGov_Users.Password != loginModel.Password)
{
ModelState.AddModelError("", "Invalid Password");
return View();
}
var loginDetail = new LoginDetails();
var serializer = new JavaScriptSerializer();
loginDetail.userID = eGov_Users.UserId;
loginDetail.username = eGov_Users.UserName;
loginDetail.firstName = eGov_Users.FirstName;
loginDetail.lastName = eGov_Users.LastName;
var userData = SerializeUserInfoInternal(loginDetail);
FormsAuthentication.SetAuthCookie(loginDetail.username, false);
var cookie = FormsAuthentication.GetAuthCookie(
FormsAuthentication.FormsCookieName, false);
var ticket = FormsAuthentication.Decrypt(cookie.Value);
var durationInHours = 8;
FormsAuthenticationTicket newTicket = new FormsAuthenticationTicket(
ticket.Version,
loginDetail.username,
DateTime.Now,
DateTime.Now.AddHours(durationInHours),
true,
userData);
// Encrypt the ticket.
string encTicket = FormsAuthentication.Encrypt(newTicket);
cookie.Value = encTicket;
Response.Cookies.Add(cookie);
int cookieSize = System.Text.UTF8Encoding.UTF8.GetByteCount(cookie.Values.ToString());
Session["CookieSize"] = cookieSize;
if (string.IsNullOrEmpty(ReturnUrl))
{
return RedirectToAction("Index", "Users");
}
}
}
return RedirectToAction("Login", "Login");
}
GLOBAL ASAX
protected void Application_PostAuthenticateRequest()
{
var ticket = GetTicketFromCurrentCookie();
if (ticket == null)
{
Global.WriteLog("Application_PostAuthenticateRequest", "ticket becomes null");
return;
}
var user = DeserializeUserInfoInternal(ticket.Name, ticket.UserData);
if (user == null)
{
return;
}
var principal = new AppUserPrincipal(user);
HttpContext.Current.User = principal;
}
private static FormsAuthenticationTicket GetTicketFromCurrentCookie()
{
var cookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
if (cookie == null)
{
Global.WriteLog("GetTicketFromCurrentCookie", "Cookie becomes null");
return null;
}
var ticket = FormsAuthentication.Decrypt(cookie.Value);
return ticket;
}
static LoginDetails DeserializeUserInfoInternal(string name, string userData)
{
var deserialize = new JavaScriptSerializer();
var loginDetails = deserialize.Deserialize<LoginDetails>(userData);
return loginDetails;
}
use below code to get the cookie value
HttpContext.Current.Request.Cookies.Get(".ASPXAUTH");

Accessing Google Docs with GData

Working Platform: ASP.NET 4.0 C# ( Framework Agnostic )
Google GData is my dependency
using Google.GData.Client;
using Google.GData.Extensions;
using Google.GData.Documents;
I have two pages Auth and List.
Auth redirects to Google Server like this
public ActionResult Auth()
{
var target = Request.Url.ToString().ToLowerInvariant().Replace("auth", "list");
var scope = "https://docs.google.com/feeds/";
bool secure = false, session = true;
var authSubUrl = AuthSubUtil.getRequestUrl(target, scope, secure, session);
return new RedirectResult(authSubUrl);
}
Now it reaches the List Page if Authentication is successful.
public ActionResult List()
{
if (Request.QueryString["token"] != null)
{
String singleUseToken = Request.QueryString["token"];
string consumerKey = "www.blahblah.net";
string consumerSecret = "my_key";
string sessionToken = AuthSubUtil.exchangeForSessionToken(singleUseToken, null).ToString();
var authFactory = new GOAuthRequestFactory("writely", "qwd-asd-01");
authFactory.Token = sessionToken;
authFactory.ConsumerKey = consumerKey;
authFactory.ConsumerSecret = consumerSecret;
//authFactory.TokenSecret = "";
try
{
var service = new DocumentsService(authFactory.ApplicationName) { RequestFactory = authFactory };
var query = new DocumentsListQuery();
query.Title = "project";
var feed = service.Query(query);
var result = feed.Entries.ToList().ConvertAll(a => a.Title.Text);
return View(result);
}
catch (GDataRequestException gdre)
{
throw;
}
}
}
This fails at the line var feed = service.Query(query); with the error
Execution of request failed: https://docs.google.com/feeds/default/private/full?title=project
The HttpStatusCode recieved on the catch block is HttpStatusCode.Unauthorized
What is wrong with this code? Do I need to get TokenSecret? If so how?
You need to request a token from Google and use it to intialize your DocumentsService instance.
Here's an example using Google's ContactsService. It should be the same for the DocumentsService.
Service service = new ContactsService("My Contacts Application");
service.setUserCredentials("your_email_address_here#gmail.com", "yourpassword");
var token = service.QueryClientLoginToken();
service.SetAuthenticationToken(token);
But as you mentioned, you are using AuthSub. I jumped the gun a bit too fast.
I see that you are requesting a session token. According to the documentation of the API you must use the session token to authenticate requests to the service by placing the token in the Authorization header. After you've set the session token, you can use the Google Data APIs client library.
Here's a complete example (by Google) on how to use AuthSub with the .NET client library:
http://code.google.com/intl/nl-NL/apis/gdata/articles/authsub_dotnet.html
Let me include a shortened example:
GAuthSubRequestFactory authFactory =
new GAuthSubRequestFactory("cl", "TesterApp");
authFactory.Token = (String) Session["token"];
CalendarService service = new CalendarService(authFactory.ApplicationName);
service.RequestFactory = authFactory;
EventQuery query = new EventQuery();
query.Uri = new Uri("http://www.google.com/calendar/feeds/default/private/full");
EventFeed calFeed = service.Query(query);
foreach (Google.GData.Calendar.EventEntry entry in calFeed.Entries)
{
//...
}
And if I see correctly your example code pretty follows the same steps, except that you set the ConsumerKey and ConsumerSecret for the AuthFactory which is not done in the example by Google.
Used the 3-legged OAuth in the Google Data Protocol Client Libraries
Sample Code
string CONSUMER_KEY = "www.bherila.net";
string CONSUMER_SECRET = "RpKF7ykWt8C6At74TR4_wyIb";
string APPLICATION_NAME = "bwh-wssearch-01";
string SCOPE = "https://docs.google.com/feeds/";
public ActionResult Auth()
{
string callbackURL = String.Format("{0}{1}", Request.Url.ToString(), "List");
OAuthParameters parameters = new OAuthParameters()
{
ConsumerKey = CONSUMER_KEY,
ConsumerSecret = CONSUMER_SECRET,
Scope = SCOPE,
Callback = callbackURL,
SignatureMethod = "HMAC-SHA1"
};
OAuthUtil.GetUnauthorizedRequestToken(parameters);
string authorizationUrl = OAuthUtil.CreateUserAuthorizationUrl(parameters);
Session["parameters"] = parameters;
ViewBag.AuthUrl = authorizationUrl;
return View();
}
public ActionResult List()
{
if (Session["parameters"] != null)
{
OAuthParameters parameters = Session["parameters"] as OAuthParameters;
OAuthUtil.UpdateOAuthParametersFromCallback(Request.Url.Query, parameters);
try
{
OAuthUtil.GetAccessToken(parameters);
GOAuthRequestFactory authFactory = new GOAuthRequestFactory("writely", APPLICATION_NAME, parameters);
var service = new DocumentsService(authFactory.ApplicationName);
service.RequestFactory = authFactory;
var query = new DocumentsListQuery();
//query.Title = "recipe";
var feed = service.Query(query);
var docs = new List<string>();
foreach (DocumentEntry entry in feed.Entries)
{
docs.Add(entry.Title.Text);
}
//var result = feed.Entries.ToList().ConvertAll(a => a.Title.Text);
return View(docs);
}
catch (GDataRequestException gdre)
{
HttpWebResponse response = (HttpWebResponse)gdre.Response;
//bad auth token, clear session and refresh the page
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
Session.Clear();
Response.Write(gdre.Message);
}
else
{
Response.Write("Error processing request: " + gdre.ToString());
}
throw;
}
}
else
{
return RedirectToAction("Index");
}
}
This 2-legged sample never worked for me for google docs.

Categories

Resources