how to check Validate JWE Token On server - c#

its config jwt Program.cs
#region JWT
IdentityModelEventSource.ShowPII = true;
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options => {
var secretkey = Encoding.UTF8.GetBytes(builder.Configuration["JWT:Secret"]);
var encryptionkey = Encoding.UTF8.GetBytes(builder.Configuration["JWT:Encryptkey"]);
var validationParameters = new TokenValidationParameters
{
ClockSkew = TimeSpan.Zero, // default: 5 min
RequireSignedTokens = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(secretkey),
RequireExpirationTime = true,
ValidateLifetime = true,
ValidateAudience = true, //default : false
ValidAudience = builder.Configuration["JWT:ValidAudience"],
ValidateIssuer = true, //default : false
ValidIssuer = builder.Configuration["JWT:ValidIssuer"],
TokenDecryptionKey = new SymmetricSecurityKey(encryptionkey)
};
options.RequireHttpsMetadata = false;
options.SaveToken = true;
options.TokenValidationParameters = validationParameters;
});
#endregion
its my code for create JWT Secure Token
i use SecurityAlgorithms.Aes128KW for hashing
private Tuple<string, DateTime> GenerateWebTokenJwt(User userInfo)
{
IList<string> userRoles = null;
userRoles = _userManager.GetRolesAsync(userInfo).Result;
if (userRoles == null)
{
return new Tuple<string, DateTime>("", DateTime.Now);
}
var exportTime = DateTime.Now.AddHours(24);
IDictionary<string, object> authClaims = new Dictionary<string, object>();
authClaims.Add(JwtRegisteredClaimNames.Sub, userInfo.FirstName);
authClaims.Add(JwtRegisteredClaimNames.Email, userInfo.Email);
authClaims.Add(JwtRegisteredClaimNames.Name, userInfo.UserName);
authClaims.Add(JwtRegisteredClaimNames.Aud, userInfo.PhoneNumber);
authClaims.Add(JwtRegisteredClaimNames.Sid, userInfo.Id.ToString());
authClaims.Add(JwtRegisteredClaimNames.Exp, exportTime.Date.Year + "/" + exportTime.Month + "/" + exportTime.Date
+ "-" + exportTime.Hour + ":" + exportTime.Minute + ":" + exportTime.Second);
authClaims.Add(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString());
if (userRoles != null)
{
for (int i = 0; i < userRoles.Count; i++)
{
authClaims.Add(ClaimTypes.Role + i, userRoles[i]);
}
}
var encryptionkey = Encoding.UTF8.GetBytes(_configuration["JWT:Encryptkey"]); //must be 16 character
var encryptingCredentials = new EncryptingCredentials(new SymmetricSecurityKey(encryptionkey), SecurityAlgorithms.Aes128KW, SecurityAlgorithms.Aes128CbcHmacSha256);
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JWT:Secret"]));
var Credemtials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var descriptor = new SecurityTokenDescriptor
{
Issuer = _configuration["JWT:ValidIssuer"],
Audience = _configuration["JWT:ValidAudience"],
IssuedAt = DateTime.Now,
NotBefore = DateTime.Now.AddMinutes(Convert.ToInt32(_configuration["JWT:NotBeforeMinutes"])),
Expires = DateTime.Now.AddMinutes(Convert.ToInt32(_configuration["JWT:ExpirationMinutes"])),
SigningCredentials = Credemtials,
EncryptingCredentials = encryptingCredentials,
Claims = authClaims,
};
var tokenHandler = new JwtSecurityTokenHandler();
var securityToken = tokenHandler.CreateToken(descriptor);
string encryptedJwt = tokenHandler.WriteToken(securityToken);
return new Tuple<string, DateTime>(encryptedJwt, securityToken.ValidTo); ;
}
i can not use CreateJwtSecurityToken
var securityToken = tokenHandler.CreateJwtSecurityToken(descriptor);
its true i use CreateToken ?
my code for check validation bot Show Error On Run time
private bool ValidateToken(string authToken)
{
try
{
JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
//string sToken = authToken.Substring(7, authToken.Length - 7);
if (!tokenHandler.CanReadToken(authToken))
{
return false;
}
JwtSecurityToken jwtToken = tokenHandler.ReadToken(authToken) as JwtSecurityToken;
if (jwtToken == null)
{
return false;
}
TokenValidationParameters parameters = new TokenValidationParameters()
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
RequireExpirationTime = true,
ValidAudience = _configuration["JWT:ValidAudience"],
ValidIssuer = _configuration["JWT:ValidIssuer"],
//EncryptingCredentials = encryptingCredentials,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.Default.GetBytes(_configuration["JWT:Secret"]))
};
SecurityToken securityToken;
ClaimsPrincipal principal = tokenHandler.ValidateToken(authToken, parameters, out securityToken);
if (principal == null)
{
return false;
}
}
catch (Exception ex)
{
return false;
}
return true;
}
error in validation is:
{"IDX10609: Decryption failed. No Keys tried: token: 'eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwidHlwIjoiSldUIn0.NQG56EqqvkTu0K02GYK9QIl-Sapf0tMXoWXq_ZXs99Fvzr9az0BRCA.39UAZmnrfigG4HvQxg8yrw.GbrJorcklXbnAA_VvCpHUdml2Q3MbLRbPQ8ItAcsdZnQ7vWizk2fiHVdbxoxj-9obRAEQ8m10NLC04qHg6ItnW8Pl0Awus_Fsw8kwPHKBB3LD194ifsN5T8o4CVuXQtZdNz5AcLfMRWwTIBMHs4jvP_sRRu2GQi08G0LtipKoYEKKEbk9rw0GiLEDYYVXnsFVLxNHyM9tHkmxFlbqdRTG7S6elLD1QbvCRFBbkEW7teafYE-CcxIFKb73YMPRpm1_9VpJBLCFNZSpefB4GwPpGUds0t6wVSGmKaBDyd1KCsCNgzyeYZlkmXbU-S2S7KVSU00oMpOwj3D1sUmsiWmue7nWuw3tQv-pwzVk89_xzAGecieQpY3F7QqzjTR6mkSj1xN8V65z0qQsLgA6DmrsB5AHpp4koTjqMWBMTpLKW7siY6n31GcJP6NNdADPl_hpcfuicVOeP4Zfl6RtHhePJyI4hHk-9pNkzc-i5DGSGdaTFDjg9JuLNLmGU5xKdmMXFWBUHqpgRJm1_ZlGzP2zpn7tTxXtp1l4wv4K8AzKhLmMDXullMeBAytS4-hiWNfclVttw_qLH15DEOCF11foA.xdyKycxHBsRF2jn5Y0IcPQ'."}
sample jwe Code is:
eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwidHlwIjoiSldUIn0.A0huSqmRHCi2VjCBNhzBLSCE6kNIFAERvBPMEPMUAXdDH60gvkxweg.UV6tmuaPHVpQ9H1WyFOB-w.9ljq5kzXk98WiNQrwOy-NlYb4HXC1uZx_jjn3o31qIfur_ORmOK3Uq6Zu9RcVe_IbY3A200lJMUcbeL_c5vNoUs6E1-F-8ffgpQiu-jR-lvZFsBi6_qUD7___qxl_aNBZdk1CqM5gKJeDKN9QEHm17QsH27hcF6HgHOh7-soIH0BdkYxejkpV2b3marLW9Zp6JeGsRSYxMMN4a7r1XSUp-UvIE0LBbhq0Bnz0vhzYbEALDlAwT_zSonDoMFNR5mbwIDcVMhYxwXZvxMfsAHDK4k8_nffqxFQ6foljL1ErjkT02IvUHl9uh2tsSQiOjcAfJu9oRnmUJXvFBeOKjLEa-jIwPvteXWWMJIEh_Yl_aqeSUWc_emiEt1gHlfBpCzmj3XQaPTWLSAuJDyVb6OuMjDlZ8l830SO6vkbfI3q0WtYziuB4hLmm1S4BV_2Qn9KemxWdDN82YfsQg5Skr0mEX4VIXu-FDsako1VEQtxygNLrngLG5iI-RRmXhlhqEy0nkQCup7SqYo45DZkFsJZKQJ8Bs0BCZjacjndK5_vG504O06ui5v8kbTixjp6xjyyEGeALtjMeRSW3y-YUvuwWw.kCx6iQOPEasw395EIpcZhg

I Find awnser in https://www.scottbrady91.com/c-sharp/json-web-encryption-jwe-in-dotnet-core
ty Scott Brady
private bool ValidateToken(string authToken)
{
try
{
var tokenHandler = new JwtSecurityTokenHandler();
if (!tokenHandler.CanReadToken(authToken))
{
return false;
}
JwtSecurityToken jwtToken = tokenHandler.ReadToken(authToken) as JwtSecurityToken;
if (jwtToken == null)
{
return false;
}
var encryptionkey = Encoding.UTF8.GetBytes(_configuration["JWT:Encryptkey"]); //must be 16 character
var encryptingCredentials = new EncryptingCredentials(new SymmetricSecurityKey(encryptionkey), SecurityAlgorithms.Aes128KW, SecurityAlgorithms.Aes128CbcHmacSha256);
ClaimsPrincipal claimsPrincipal = tokenHandler.ValidateToken(
authToken,
new TokenValidationParameters
{
ValidateLifetime = true,
ValidAudience = _configuration["JWT:ValidAudience"],
ValidIssuer = _configuration["JWT:ValidIssuer"],
RequireSignedTokens = false,
TokenDecryptionKey = new SymmetricSecurityKey(encryptionkey),
},
out SecurityToken securityToken);
if (claimsPrincipal == null)
{
return false;
}
}
catch (Exception ex)
{
return false;
}
return true;
}
and Create JWE Token Code is:
private Tuple<string, DateTime> GenerateWebTokenJwt(User userInfo)
{
IList<string> userRoles = null;
userRoles = _userManager.GetRolesAsync(userInfo).Result;
if (userRoles == null)
{
return new Tuple<string, DateTime>("", DateTime.Now);
}
var exportTime = DateTime.Now.AddHours(24);
IDictionary<string, object> authClaims = new Dictionary<string, object>();
authClaims.Add(JwtRegisteredClaimNames.Sub, userInfo.FirstName);
authClaims.Add(JwtRegisteredClaimNames.Email, userInfo.Email);
authClaims.Add(JwtRegisteredClaimNames.Name, userInfo.UserName);
authClaims.Add(JwtRegisteredClaimNames.Aud, userInfo.PhoneNumber);
authClaims.Add(JwtRegisteredClaimNames.Sid, userInfo.Id.ToString());
authClaims.Add(JwtRegisteredClaimNames.Exp, exportTime.Date.Year + "/" + exportTime.Month + "/" + exportTime.Date
+ "-" + exportTime.Hour + ":" + exportTime.Minute + ":" + exportTime.Second);
authClaims.Add(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString());
if (userRoles != null)
{
for (int i = 0; i < userRoles.Count; i++)
{
authClaims.Add(ClaimTypes.Role + i, userRoles[i]);
}
}
var handler = new JwtSecurityTokenHandler();
var encryptionkey = Encoding.UTF8.GetBytes(_configuration["JWT:Encryptkey"]); //must be 16 character
var encryptingCredentials = new EncryptingCredentials(new SymmetricSecurityKey(encryptionkey), SecurityAlgorithms.Aes128KW, SecurityAlgorithms.Aes128CbcHmacSha256);
var tokenDescriptor = new SecurityTokenDescriptor
{
Audience = _configuration["JWT:ValidAudience"],
Issuer = _configuration["JWT:ValidIssuer"],
Expires = DateTime.Now.AddMinutes(Convert.ToInt32(_configuration["JWT:ExpirationMinutes"])),
Subject = new ClaimsIdentity(new List<Claim> { new Claim("sub", "scott") }),
EncryptingCredentials = encryptingCredentials
};
string token = handler.CreateEncodedJwt(tokenDescriptor);
return new Tuple<string, DateTime>(token, DateTime.Now.AddMinutes(Convert.ToInt32(_configuration["JWT:ExpirationMinutes"])));
}

Related

how to get automatically new JWT Token and Refresh Token in angular

I am using angular and asp.net core I have implemented JWT Token and refresh token with the help of
this artical I have written code to check Jwt is valid in Authguard if jwt is not valid so with refresh token will make call to api and get the new Jwt and refresh token . this scenario is happing only if I am performing any event like refreshing page and navigating to another page. I want to know is there any way that if our jwt token expire without performing any event angular detect it and automatically call to api with refersh token and get the new jwt token
here are codes
authguard
here in authguard isUserAuthenticated() is going to check jwt expired or not if expired it will make api call with refresh token and get the new jwt and refresh token
async canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Promise<boolean> {
if((await this._authService.isUserAuthenticated()) === true){
return true;
}
else{
this._router.navigate([RouteConstant.SIGN_IN]);
return false;
}
}
public async isUserAuthenticated(){
if(this._storageService.getToken() !== null){
if(!this._jwtHelperService.isTokenExpired(this._storageService.getToken()?.toString())){
return true;
}
else{
if(!this._storageService.refreshTokenExists()){
return false;
}
else{
let authTokenClient: AuthTokenClient = {
token: this._storageService.getToken() as string,
refreshToken: this._storageService.getRefreshToken() as string
};
let response: AuthToken = await this._serverService.refreshAuthToken(authTokenClient).toPromise<AuthToken>();
console.log(response);
this.setAuth(response);
return !this._jwtHelperService.isTokenExpired(this._storageService.getToken()?.toString());
}
}
}
else{
return false;
}
}
}
public setAuth(authToken: AuthToken){
this._storageService.saveToken(authToken.token?.toString());
this._storageService.saveRefreshToken(authToken.refreshToken);
}
public async refreshAuthToken(authTokenClient: AuthTokenClient) {
const response = await this._http.post<AuthToken>(environment.apiHost + UrlConstant.ACCOUNT_REFRESH, authTokenClient,{observe: 'response'}).toPromise();
console.log(response);
const newToken = (<any>response).body.token;
console.log(newToken);
const newRefreshToken = (<any>response).body.refreshToken;
console.log(newRefreshToken);
localStorage.setItem("token", newToken);
localStorage.setItem("refreshToken",newRefreshToken);
if (newToken && newRefreshToken == null){
return false
}
else {
return true;
}
}
Startup.cs
services.AddAuthentication(opt =>
{
opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
opt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = jwtSettings.ValidIssuer,
ValidAudience = jwtSettings.ValidAudience,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration[ConfigurationKeyConstant.JWT_SECRET_KEY])),
ClockSkew = TimeSpan.Zero
};
});
Controller.cs
[Route("refresh")]
public IActionResult Refresh(AuthTokenBase authTokenBase)
{
if (authTokenBase is null)
{
return BadRequest("Invalid client request");
}
string accessToken = authTokenBase.Token;
string refreshToken = authTokenBase.RefreshToken;
var principal = this._jwtHandler.GetPrincipalFromExpiredToken(accessToken);
var username = principal.Identity.Name;
string newAccessToken = this._jwtHandler.GenerateToken();
string newRefreshToken = this._jwtHandler.GenerateRefreshToken();
return Ok(new AuthToken()
{
Token = newAccessToken,
RefreshToken = newRefreshToken,
IsAuthSuccessful = true
});
}
Under your ConfigureServices at service.AddAuthentication() you can add event for OnAuthenticationFailed.
And there check for exception type if its SecurityTokenExpiredException, then set response header tokenExpired=true.
Later at client side check response header and call refresh token API based on tokenExpired value.
pseudo code:
services.AddAuthentication(opt =>
{
opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
opt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = jwtSettings.ValidIssuer,
ValidAudience = jwtSettings.ValidAudience,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration[ConfigurationKeyConstant.JWT_SECRET_KEY])),
ClockSkew = TimeSpan.Zero
};
options.Events = new JwtBearerEvents {
OnAuthenticationFailed = context => {
if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
{
context.Response.Headers.Add("IS-TOKEN-EXPIRED", "true");
}
return Task.CompletedTask;
}
};
});
Here in response header it sets IS-TOKEN-EXPIRED as true, if token is expired.
For more detail you can check this Blog article written by me.
You can use angular interceptor.
Intercept every error in http request
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
var modifiedRequest = this.normalizeRequestHeaders(request);
return next.handle(modifiedRequest)
.pipe(
catchError(error => {
if (error instanceof HttpErrorResponse && error.status === 401) {
return this.tryGetRefreshTokenService(request, next, error);
} else {
return this.handleErrorResponse(error);
}
}),
switchMap((event) => {
return this.handleSuccessResponse(event);
})
);
}

.NET Core Jwt unable to resolve service

This is my jwt setup in Startup.cs
services.AddIdentity<User, Role>()
.AddUserManager<CustomUserManager>()
.AddEntityFrameworkStores<ManagementStudioDbContext>();
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = "bearer";
x.DefaultChallengeScheme = "bearer";
})
.AddJwtBearer("bearer",x =>
{
x.RequireHttpsMetadata = false;
x.SaveToken = true;
//x.TokenValidationParameters = tokenValidationParameters;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Environment.GetEnvironmentVariable(MSCASGlobals.MS_SecretKey))),
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidIssuer = "SDD",
ValidAudience = "SDD",
ClockSkew = TimeSpan.Zero,
};
x.Events = new JwtBearerEvents
{
OnAuthenticationFailed = context =>
{
if(context.Exception.GetType() == typeof(SecurityTokenExpiredException))
{
context.Response.Headers.Add("Token-Expired", "true");
}
return Task.CompletedTask;
}
};
});
When I try to run my Authentication API, I get this error:
Unable to resolve service for type
'Microfoft.IdentityModel.Tokens.TokenValidationparameters' while
attempting to active 'Appone.Connect.Api.Token'
This is the code to create the token:
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration[AppOne.ClassLibrary.Globals.MSCASGlobals.MS_SecretKey]));
var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(claims),
Expires = DateTime.UtcNow.AddMinutes(2),
SigningCredentials = credentials,
IssuedAt = DateTime.Now,
Issuer = "SDD",
Audience = "SDD"
};
var token = tokenHandler.CreateToken(tokenDescriptor);
var refreshToken = tokens.GenerateRefreshToken();
Connect.Api is this controller class. It is already injected in my Startup.cs
public class Token : Controller
{
public Token()
{
}
public string GenerateRefreshToken()
{
var random = new byte[64];
var rng = RandomNumberGenerator.Create();
rng.GetBytes(random);
return Convert.ToBase64String(random).Replace("/", "_").Replace("+", "_");
}
public string RefreshToken(string Token, string RefreshToken, string SecretKey)
{
var validatedToken = GetPrincipalFromToken(Token, SecretKey);
if (validatedToken == null)
{
return null;
}
return "Hello";
}
public ClaimsPrincipal GetPrincipalFromToken(string Token, string SecretKey)
{
var tokenHandler = new JwtSecurityTokenHandler();
var tokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false, //you might want to validate the audience and issuer depending on your use case
ValidateIssuer = false,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecretKey)),
ValidateLifetime = false //here we are saying that we don't care about the token's expiration date
};
try
{
var principal = tokenHandler.ValidateToken(Token, tokenValidationParameters, out var validatedToken);
if (!ValidateSecurityAlgorithm(validatedToken))
{
return null;
};
return principal;
}
catch(Exception e)
{
return null;
}
}
private bool ValidateSecurityAlgorithm(SecurityToken SecurityToken)
{
var res = (SecurityToken is JwtSecurityToken jwtSecurityToken) && jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256,StringComparison.InvariantCultureIgnoreCase);
return res;
}
}

How to validate a JWT token

I'm trying to use JWT tokens. I managed to generate a valid JWTTokenString and validated it on the JWT debugger but I'm having an impossible time validating the token in .Net. Here's the code I have so far:
class Program {
static string key = "401b09eab3c013d4ca54922bb802bec8fd5318192b0a75f201d8b3727429090fb337591abd3e44453b954555b7a0812e1081c39b740293f765eae731f5a65ed1";
static void Main(string[] args) {
var stringToken = GenerateToken();
ValidateToken(stringToken);
}
private static string GenerateToken() {
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var header = new JwtHeader(credentials);
var payload = new JwtPayload {
{ "some ", "hello "},
{ "scope", "world"},
};
var secToken = new JwtSecurityToken(header, payload);
var handler = new JwtSecurityTokenHandler();
return handler.WriteToken(secToken);
}
private static bool ValidateToken(string authToken) {
var tokenHandler = new JwtSecurityTokenHandler();
var validationParameters = GetValidationParameters();
SecurityToken validatedToken;
IPrincipal principal = tokenHandler.ValidateToken(authToken, validationParameters, out validatedToken);
Thread.CurrentPrincipal = principal;
return true;
}
private static TokenValidationParameters GetValidationParameters() {
return new TokenValidationParameters() {
//NOT A CLUE WHAT TO PLACE HERE
};
}
}
All I want is a function that receives a token and returns true or false based on its validity. From research I've seen people use IssuerSigningToken to assign the validation key. But when I try to use it, it doesn't seem to exist. Could anyone give me a hand on validating the token?
You must use the same key to validate the token as the one you use to generate it. Also you need to disable some validations such as expiration, issuer and audiance, because the token you generate doesn't have these information (or you can add these information). Here's a working example:
class Program
{
static string key = "401b09eab3c013d4ca54922bb802bec8fd5318192b0a75f201d8b3727429090fb337591abd3e44453b954555b7a0812e1081c39b740293f765eae731f5a65ed1";
static void Main(string[] args)
{
var stringToken = GenerateToken();
ValidateToken(stringToken);
}
private static string GenerateToken()
{
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var secToken = new JwtSecurityToken(
signingCredentials: credentials,
issuer: "Sample",
audience: "Sample",
claims: new[]
{
new Claim(JwtRegisteredClaimNames.Sub, "meziantou")
},
expires: DateTime.UtcNow.AddDays(1));
var handler = new JwtSecurityTokenHandler();
return handler.WriteToken(secToken);
}
private static bool ValidateToken(string authToken)
{
var tokenHandler = new JwtSecurityTokenHandler();
var validationParameters = GetValidationParameters();
SecurityToken validatedToken;
IPrincipal principal = tokenHandler.ValidateToken(authToken, validationParameters, out validatedToken);
return true;
}
private static TokenValidationParameters GetValidationParameters()
{
return new TokenValidationParameters()
{
ValidateLifetime = false, // Because there is no expiration in the generated token
ValidateAudience = false, // Because there is no audiance in the generated token
ValidateIssuer = false, // Because there is no issuer in the generated token
ValidIssuer = "Sample",
ValidAudience = "Sample",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key)) // The same key as the one that generate the token
};
}
}
In my case I only want to validate if the signature is correct. Most likely you will probably want to use #meziantou answer. But if you only want to verify that the message has not been tampered with here is an example. Lastly since I am the only person generating this tokens and I know I will be generating them using HmacSha256 I am using this approach.
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
class Program
{
static readonly byte[] key = Encoding.UTF8.GetBytes("f645b33ef0d04cbe859777ac6f46226d");
// use this algorithm for example to work
static readonly string securityAlgorithm = SecurityAlgorithms.HmacSha256;
static void Main()
{
var token = GenerateToken();
var isTokenValid = IsJwtTokenValid(token);
if (isTokenValid)
Console.WriteLine(true);
}
/// <summary>
/// This method assumes token has been hashed using HMACSHA256 algorithm!
/// </summary>
private static bool IsJwtTokenValid(string token)
{
// example of token:
// header payload signature
// eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJmb29AZ21haWwuY29tIiwiZXhwIjoxNjQ1NzM1MDU2fQ.Gtrm2G_35ynyNd1-CjZ1HsvvFFItEsXPvwhaOsN81HQ
// from JWT spec
static string Base64UrlEncode(byte[] input)
{
var output = Convert.ToBase64String(input);
output = output.Split('=')[0]; // Remove any trailing '='s
output = output.Replace('+', '-'); // 62nd char of encoding
output = output.Replace('/', '_'); // 63rd char of encoding
return output;
}
try
{
// position of second period in order to split header+payload and signature
int index = token.IndexOf('.', token.IndexOf('.') + 1);
// Example: Gtrm2G_35ynyNd1-CjZ1HsvvFFItEsXPvwhaOsN81HQ
string signature = token[(index + 1)..];
// Bytes of header + payload
// In other words bytes of: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJmb29AZ21haWwuY29tIiwiZXhwIjoxNjQ1NzM1MDU2fQ
byte[] bytesToSign = Encoding.UTF8.GetBytes(token[..index]);
// compute hash
var hash = new HMACSHA256(key).ComputeHash(bytesToSign);
var computedSignature = Base64UrlEncode(hash);
// make sure that signatures match
return computedSignature.Length == signature.Length
&& computedSignature.SequenceEqual(signature);
}
catch
{
return false;
}
}
private static string GenerateToken()
{
var securityKey = new SymmetricSecurityKey(key);
var credentials = new SigningCredentials(securityKey, securityAlgorithm);
var secToken = new JwtSecurityToken(
signingCredentials: credentials,
claims: new[]
{
new Claim(JwtRegisteredClaimNames.Sub, "foo#gmail.com")
},
expires: DateTime.UtcNow.AddDays(1));
var handler = new JwtSecurityTokenHandler();
return handler.WriteToken(secToken);
}
}
Validate Token in Jwt Middleware Class that Invoke Method fire in every request for Authorization
JwtMiddleware
{
private readonly RequestDelegate _next;
private readonly TokenValidationParameters _tokenValidationParams;
public JwtMiddleware(RequestDelegate next, TokenValidationParameters
tokenValidationParams)
{
_next = next;
_tokenValidationParams = tokenValidationParams;
}
public async Task Invoke(HttpContext context)
{
try{
var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
var jwtTokenHandler = new JwtSecurityTokenHandler();
// Validation 1 - Validation JWT token format
var tokenInVerification = jwtTokenHandler.ValidateToken(token, _tokenValidationParams, out var validatedToken);
if (validatedToken is JwtSecurityToken jwtSecurityToken)
{
var result = jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256, StringComparison.InvariantCultureIgnoreCase);
if (result == false)
{
Error Invalid = new Error()
{
Success = false,
Errors = "Token is Invalid"
};
context.Items["Error"] = Invalid;
}
}
}
catch (Exception ex)
{
Error Invalid = new Error()
{
Success = false,
Errors = "Token does not match or may expired."
};
context.Items["Error"] = Invalid ; // userService.GetById(userId);
}
await _next(context);
}
}

asp.net mvc and apx in one project with owin

I need to add an aspx page into my mvc project, but I always get 404. I founded that in web.config there is owin http handler registration
<add name="Owin" verb="" path="*" type="Microsoft.Owin.Host.SystemWeb.OwinHttpHandler, Microsoft.Owin.Host.SystemWeb" />
If I commented this my aspx page works fine.
So where can be problem?
Here is my Startup's code:
public void Configuration(IAppBuilder app)
{
app.UseStaticFiles();
Dictionary<string, string> mapping = new Dictionary<string, string>
{
{".apk", "application/vnd.android.package-archive"}
};
StaticFileOptions options = new StaticFileOptions
{
ContentTypeProvider = new FileExtensionContentTypeProvider(mapping)
};
app.UseStaticFiles(options);
LogProvider.SetCurrentLogProvider(new Log4NetLogProvider());
HttpConfiguration config = new HttpConfiguration();
ODataConventionModelBuilder builder = new ODataConventionModelBuilder
{
Namespace = "blabla",
ContainerName = "blabla"
};
builder.EntitySet<Common.DataAccess.Sharepoint.Users>("Users");
var departmentSet = builder.EntitySet<Common.DataAccess.Sharepoint.Departments>("Department");
builder.EntitySet<Common.DataAccess.Sharepoint.Roles>("Roles");
var dependentFunction = departmentSet.EntityType.Collection.Function("GetChilds");
dependentFunction.Parameter<int>("parent");
dependentFunction.ReturnsCollectionFromEntitySet<Common.DataAccess.Sharepoint.Departments>("Department");
var allUsers = departmentSet.EntityType.Collection.Function("GetAllDependenUsers");
allUsers.Parameter<int>("department");
allUsers.Parameter<string>("query");
allUsers.ReturnsCollectionFromEntitySet<Common.DataAccess.Sharepoint.Users>("Users");
config.MapODataServiceRoute("orgstructure", "orgstructure", builder.GetEdmModel());
app.UseWebApi(config);
AntiForgeryConfig.UniqueClaimTypeIdentifier = Constants.ClaimTypes.Subject;
JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();
SecureString password = new SecureString();
foreach (char c in "idsrv3test")
password.AppendChar(c);
IdentityServerOptions identityServerOptions = new IdentityServerOptions
{
SiteName = "123123",
Factory = new IdentityServerServiceFactory
{
UserService = new Registration<IUserService>(typeof (IdentityUserService)),
ScopeStore = new Registration<IScopeStore>(typeof (IdentityScopeStore)),
ClientStore = new Registration<IClientStore>(typeof (IdentityClientStore))
},
RequireSsl = false,
CspOptions = new CspOptions
{
Enabled = false
},
SigningCertificate = new X509Certificate2(Assets.idsrv3test, password),
CorsPolicy = CorsPolicy.AllowAll,
AuthenticationOptions = {EnablePostSignOutAutoRedirect = true}
};
identityServerOptions.Factory.Register(new Registration<IUserRoleService, UserRolesService>());
identityServerOptions.Factory.Register(new Registration<IUserDepartmentsService, UserDepartmentsService>());
app.Map("/open_id", idsrvApp =>
{
identityServerOptions.Factory.ConfigureDefaultViewService(new DefaultViewServiceOptions
{
CacheViews = true,
ViewLoader = new Registration<IViewLoader, IdentityViewLoader>()
});
idsrvApp.UseIdentityServer(identityServerOptions);
});
app.UseCookieAuthentication(new CookieAuthenticationOptions {AuthenticationType = "Cookies"});
string baseUrl = ConfigurationManager.AppSettings["RedirectUrl"];
string clientName = ConfigurationManager.AppSettings["ClientName"];
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append(Constants.StandardScopes.OpenId);
stringBuilder.Append(" ");
stringBuilder.Append(Common.Constants.Scopes.Portal);
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
Authority = baseUrl + "open_id",
ClientId = clientName,
Scope = stringBuilder.ToString(),
ResponseType = "id_token",
SignInAsAuthenticationType = "Cookies",
RedirectUri = baseUrl,
UseTokenLifetime = false,
Notifications = new OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated = n =>
{
var id = n.AuthenticationTicket.Identity;
// create new identity and set name and role claim type
var nid = new ClaimsIdentity(id.AuthenticationType, ClaimTypes.GivenName, ClaimTypes.Role);
IEnumerable<Claim> claims = id.Claims
.Where(x => x.Type != Common.Constants.Claims.Role && x.Type != Common.Constants.Claims.PortalRole);
nid.AddClaims(claims);
nid.AddClaims(id.Claims.Where(x => x.Type == Common.Constants.Claims.Role)
.Select(x => new Claim(ClaimTypes.Role, x.Value, x.ValueType, x.Issuer, x.OriginalIssuer))
);
nid.AddClaims(id.Claims.Where(x => x.Type == Common.Constants.Claims.PortalRole)
.Select(x => new Claim(ClaimTypes.Role, x.Value, x.ValueType, x.Issuer, x.OriginalIssuer))
);
nid.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));
n.AuthenticationTicket = new AuthenticationTicket(nid, n.AuthenticationTicket.Properties);
return Task.FromResult(new object());
},
RedirectToIdentityProvider = n =>
{
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.AuthenticationRequest)
{
var baseAddress = new Uri(n.ProtocolMessage.IssuerAddress);
if (baseAddress.Host != n.Request.Uri.Host)
{
var uriBuilder = new UriBuilder(baseAddress) {Host = n.Request.Uri.Host};
n.ProtocolMessage.IssuerAddress = uriBuilder.Uri.ToString();
}
var redirectBaseAddress = new Uri(n.ProtocolMessage.RedirectUri);
if (redirectBaseAddress.Host != n.Request.Uri.Host)
{
var uriBuilder = new UriBuilder(redirectBaseAddress) { Host = n.Request.Uri.Host };
n.ProtocolMessage.RedirectUri = uriBuilder.Uri.ToString();
}
}
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.LogoutRequest)
{
var idTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token").Value;
n.ProtocolMessage.IdTokenHint = idTokenHint;
}
return Task.FromResult(new object());
}
}
});
}
As long as you will use OwinHttpHandler you will not be able to serve aspx pages. This happens because instead of letting aspx request to reach webforms handler, you impose it to be processed by OwinHttpHandler that doesn't know how to handle aspx. So the solution, as you empirically saw, is to eliminate handler adding from web.config.

Google Calendar API, Adding an event to someones calendar just by knowing their e-mail address

I have downloaded the Google.Apis namespace:
using Google.Apis.Auth.OAuth2;
using Google.Apis.Calendar.v3;
using Google.Apis.Calendar.v3.Data;
using Google.Apis.Services;
I've spent the entire day looking on the web to see .NET samples about how I can possible add an event to someones calendar just by knowing their e-mail address.
I tried the following code, but it's bringing up errors and it's quite obvious that it isn't going to work:
Public void Method(string email, string text)
{
UserCredential credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets
{
ClientId = "CLIENTID",
ClientSecret = "CLIENTSECRET",
},
new[] { CalendarService.Scope.Calendar },
"user",
CancellationToken.None).Result;
// Create the service.
var service = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Calendar API Sample",
});
Event event1 = new Event()
{
Summary = "Something",
Location = "Somewhere",
Start = new EventDateTime() {
DateTime = DateTime.Now,
TimeZone = "America/Los_Angeles"
},
End = new EventDateTime() {
DateTime = DateTime.Now,
TimeZone = "America/Los_Angeles"
},
Attendees = new List<EventAttendee>()
{
new EventAttendee() { Email: email } //bringing up an error "Syntax ',' expected
}
};
Event thisevent = service.Events.Insert(event1, "primary").Fetch(); // Another error. "Does not contain a definition for Fetch"
}
Any help is appreciated! Even samples of other code :)
There are syntax errors in the part where you create the event and insert it. Here is a snippet that has correct syntax for the Google API .NET library:
Event myEvent = new Event
{
Summary = "Appointment",
Location = "Somewhere",
Start = new EventDateTime() {
DateTime = new DateTime(2014, 6, 2, 10, 0, 0),
TimeZone = "America/Los_Angeles"
},
End = new EventDateTime() {
DateTime = new DateTime(2014, 6, 2, 10, 30, 0),
TimeZone = "America/Los_Angeles"
},
Recurrence = new String[] {
"RRULE:FREQ=WEEKLY;BYDAY=MO"
},
Attendees = new List<EventAttendee>()
{
new EventAttendee() { Email = "johndoe#gmail.com" }
}
};
Event recurringEvent = service.Events.Insert(myEvent, "primary").Execute();
public static string ApplicationName = "Google Console ApplicationName";
public static string ClientId = "Google Console ClientId";
public static string ClientSecret = "Google Console ClientSecret";
public static string RedirectURL = "Google Console RedirectURL";
public static ClientSecrets GoogleClientSecrets = new ClientSecrets()
{
ClientId = ClientId,
ClientSecret = ClientSecret
};
public static string[] Scopes =
{
CalendarService.Scope.Calendar,
CalendarService.Scope.CalendarReadonly
};
public static UserCredential GetUserCredential(out string error)
{
UserCredential credential = null;
error = string.Empty;
try
{
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets
{
ClientId = ClientId,
ClientSecret = ClientSecret
},
Scopes,
Environment.UserName,
CancellationToken.None,
null).Result;
}
catch (Exception ex)
{
credential = null;
error = "Failed to UserCredential Initialization: " + ex.ToString();
}
return credential;
}
public static IAuthorizationCodeFlow GoogleAuthorizationCodeFlow(out string error)
{
IAuthorizationCodeFlow flow = null;
error = string.Empty;
try
{
flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = GoogleClientSecrets,
Scopes = Scopes
});
}
catch (Exception ex)
{
flow = null;
error = "Failed to AuthorizationCodeFlow Initialization: " + ex.ToString();
}
return flow;
}
public static UserCredential GetGoogleUserCredentialByRefreshToken(string refreshToken, out string error)
{
TokenResponse respnseToken = null;
UserCredential credential = null;
string flowError;
error = string.Empty;
try
{
// Get a new IAuthorizationCodeFlow instance
IAuthorizationCodeFlow flow = GoogleAuthorizationCodeFlow(out flowError);
respnseToken = new TokenResponse() { RefreshToken = refreshToken };
// Get a new Credential instance
if ((flow != null && string.IsNullOrWhiteSpace(flowError)) && respnseToken != null)
{
credential = new UserCredential(flow, "user", respnseToken);
}
// Get a new Token instance
if (credential != null)
{
bool success = credential.RefreshTokenAsync(CancellationToken.None).Result;
}
// Set the new Token instance
if (credential.Token != null)
{
string newRefreshToken = credential.Token.RefreshToken;
}
}
catch (Exception ex)
{
credential = null;
error = "UserCredential failed: " + ex.ToString();
}
return credential;
}
public static CalendarService GetCalendarService(string refreshToken, out string error)
{
CalendarService calendarService = null;
string credentialError;
error = string.Empty;
try
{
var credential = GetGoogleUserCredentialByRefreshToken(refreshToken, out credentialError);
if (credential != null && string.IsNullOrWhiteSpace(credentialError))
{
calendarService = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName
});
}
}
catch (Exception ex)
{
calendarService = null;
error = "Calendar service failed: " + ex.ToString();
}
return calendarService;
}
public static string AddCalenderEvents(string refreshToken, string emailAddress, string summary, DateTime? start, DateTime? end, out string error)
{
string eventId = string.Empty;
error = string.Empty;
string serviceError;
try
{
var calendarService = GetCalendarService(refreshToken, out serviceError);
if (calendarService != null && string.IsNullOrWhiteSpace(serviceError))
{
var list = calendarService.CalendarList.List().Execute();
var calendar = list.Items.SingleOrDefault(c => c.Summary == emailAddress);
if (calendar != null)
{
Google.Apis.Calendar.v3.Data.Event calenderEvent = new Google.Apis.Calendar.v3.Data.Event();
calenderEvent.Summary = summary;
//calenderEvent.Description = summary;
//calenderEvent.Location = summary;
calenderEvent.Start = new Google.Apis.Calendar.v3.Data.EventDateTime
{
//DateTime = new DateTime(2018, 1, 20, 19, 00, 0)
DateTime = start//,
//TimeZone = "Europe/Istanbul"
};
calenderEvent.End = new Google.Apis.Calendar.v3.Data.EventDateTime
{
//DateTime = new DateTime(2018, 4, 30, 23, 59, 0)
DateTime = start.Value.AddHours(12)//,
//TimeZone = "Europe/Istanbul"
};
calenderEvent.Recurrence = new List<string>();
//Set Remainder
calenderEvent.Reminders = new Google.Apis.Calendar.v3.Data.Event.RemindersData()
{
UseDefault = false,
Overrides = new Google.Apis.Calendar.v3.Data.EventReminder[]
{
new Google.Apis.Calendar.v3.Data.EventReminder() { Method = "email", Minutes = 24 * 60 },
new Google.Apis.Calendar.v3.Data.EventReminder() { Method = "popup", Minutes = 24 * 60 }
}
};
#region Attendees
//Set Attendees
calenderEvent.Attendees = new Google.Apis.Calendar.v3.Data.EventAttendee[] {
new Google.Apis.Calendar.v3.Data.EventAttendee() { Email = "kaptan.cse#gmail.com" },
new Google.Apis.Calendar.v3.Data.EventAttendee() { Email = emailAddress }
};
#endregion
var newEventRequest = calendarService.Events.Insert(calenderEvent, calendar.Id);
newEventRequest.SendNotifications = true;
var eventResult = newEventRequest.Execute();
eventId = eventResult.Id;
}
}
}
catch (Exception ex)
{
eventId = string.Empty;
error = ex.Message;
}
return eventId;
}
public static Google.Apis.Calendar.v3.Data.Event UpdateCalenderEvents(string refreshToken, string emailAddress, string summary, DateTime? start, DateTime? end, string eventId, out string error)
{
Google.Apis.Calendar.v3.Data.Event eventResult = null;
error = string.Empty;
string serviceError;
try
{
var calendarService = GetCalendarService(refreshToken, out serviceError);
if (calendarService != null)
{
var list = calendarService.CalendarList.List().Execute();
var calendar = list.Items.SingleOrDefault(c => c.Summary == emailAddress);
if (calendar != null)
{
// Define parameters of request
EventsResource.ListRequest request = calendarService.Events.List("primary");
request.TimeMin = DateTime.Now;
request.ShowDeleted = false;
request.SingleEvents = true;
request.MaxResults = 10;
request.OrderBy = EventsResource.ListRequest.OrderByEnum.StartTime;
// Get selected event
Google.Apis.Calendar.v3.Data.Events events = request.Execute();
var selectedEvent = events.Items.FirstOrDefault(c => c.Id == eventId);
if (selectedEvent != null)
{
selectedEvent.Summary = summary;
selectedEvent.Start = new Google.Apis.Calendar.v3.Data.EventDateTime
{
DateTime = start
};
selectedEvent.End = new Google.Apis.Calendar.v3.Data.EventDateTime
{
DateTime = start.Value.AddHours(12)
};
selectedEvent.Recurrence = new List<string>();
// Set Remainder
selectedEvent.Reminders = new Google.Apis.Calendar.v3.Data.Event.RemindersData()
{
UseDefault = false,
Overrides = new Google.Apis.Calendar.v3.Data.EventReminder[]
{
new Google.Apis.Calendar.v3.Data.EventReminder() { Method = "email", Minutes = 24 * 60 },
new Google.Apis.Calendar.v3.Data.EventReminder() { Method = "popup", Minutes = 24 * 60 }
}
};
// Set Attendees
selectedEvent.Attendees = new Google.Apis.Calendar.v3.Data.EventAttendee[]
{
new Google.Apis.Calendar.v3.Data.EventAttendee() { Email = "kaptan.cse#gmail.com" },
new Google.Apis.Calendar.v3.Data.EventAttendee() { Email = emailAddress }
};
}
var updateEventRequest = calendarService.Events.Update(selectedEvent, calendar.Id, eventId);
updateEventRequest.SendNotifications = true;
eventResult = updateEventRequest.Execute();
}
}
}
catch (Exception ex)
{
eventResult = null;
error = ex.ToString();
}
return eventResult;
}
public static void DeletCalendarEvents(string refreshToken, string emailAddress, string eventId, out string error)
{
string result = string.Empty;
error = string.Empty;
string serviceError;
try
{
var calendarService = GetCalendarService(refreshToken, out serviceError);
if (calendarService != null)
{
var list = calendarService.CalendarList.List().Execute();
var calendar = list.Items.FirstOrDefault(c => c.Summary == emailAddress);
if (calendar != null)
{
// Define parameters of request
EventsResource.ListRequest request = calendarService.Events.List("primary");
request.TimeMin = DateTime.Now;
request.ShowDeleted = false;
request.SingleEvents = true;
request.MaxResults = 10;
request.OrderBy = EventsResource.ListRequest.OrderByEnum.StartTime;
// Get selected event
Google.Apis.Calendar.v3.Data.Events events = request.Execute();
var selectedEvent = events.Items.FirstOrDefault(c => c.Id == eventId);
if (selectedEvent != null)
{
var deleteEventRequest = calendarService.Events.Delete(calendar.Id, eventId);
deleteEventRequest.SendNotifications = true;
deleteEventRequest.Execute();
}
}
}
}
catch (Exception ex)
{
result = string.Empty;
error = ex.ToString();
}
}
public static void Run()
{
string refreshToken = string.Empty;
string credentialError;
var credential = GetUserCredential(out credentialError);
if (credential != null && string.IsNullOrWhiteSpace(credentialError))
{
//Save RefreshToken into Database
refreshToken = credential.Token.RefreshToken;
}
string addEventError;
string calendarEventId = string.Empty;
calendarEventId = AddCalenderEvents(refreshToken, "kaptan.cse#gmail.com", "My Calendar Event", DateTime.Now, DateTime.Now.AddHours(12), out addEventError);
string updateEventError;
if (!string.IsNullOrEmpty(calendarEventId))
{
UpdateCalenderEvents(refreshToken, "kaptan.cse#gmail.com", "Modified Calendar Event ", DateTime.Now, DateTime.Now.AddDays(1), calendarEventId, out updateEventError);
}
string deleteEventError;
if (!string.IsNullOrEmpty(calendarEventId))
{
DeletCalendarEvents(refreshToken, "kaptan.cse#gmail.com", calendarEventId, out deleteEventError);
}
}
}

Categories