ASP.net client and ODataController issues - asp.net

I have an ODataController with 2 methods:
[EnableQuery]
public SingleResult<item> Getitem([FromODataUri] System.Guid key)
{
return SingleResult.Create(db.items.Where(item=> item.guid == key));
}
public async Task<IHttpActionResult> Postitem([FromODataUri] System.Guid key, [FromBody] double itemId)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
//Find the correct account
item i = await db.items.FirstAsync(item=> item.guid == key);
if (i == null)
{
return NotFound();
}
//Update the account field we are using for id
i.itemId = itemId;
//Save the changes
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!itemExists(key))
{
return NotFound();
}
else
{
throw;
}
}
return Updated(i);
}
private bool itemExists(System.Guid key)
{
return db.items.Count(e => e.guid == key) > 0;
}
With the standard WebApiConfig:
GlobalConfiguration.Configuration.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
config.EnableCors();
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<item>("items");
config.Routes.MapODataServiceRoute("odata", "odata", builder.GetEdmModel());
And I can type mydomain.com/odata/items(guid'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX') in the url bar and get the database object as json just fine.
But when I try the following client code:
var itemGuid = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX";
var itemId = "55555";
using (WebClient wc = new WebClient())
{
wc.UseDefaultCredentials = true;
var domain = "mydomain.com";
var url = domain + "/odata/items(guid'" + itemGuid + "')";
var data = new NameValueCollection();
data["itemId"] = itemId;
byte[] temp = wc.UploadValues(url, "POST", data);
context.Response.Write(Encoding.UTF8.GetString(temp));
}
I get The remote server returned an error: (404) Not Found.
I know it's probably some simple mistake, but been messing around with it too long and I'm new to asp.net.

I think I may have figured it out and it makes sense. So if you create an OdataController you sorta just have your default Post to create a new item, Patch to change/update an update, Put to replace an item with a new one...
Then if you want custom api calls you need to use actions: http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/odata-v3/odata-actions
So I was just doing it all wrong. I just assumed you could put Post in front of some function name and it would be a Post opperation with custom arguments.

Related

Razorpay payment gateway integration asp.net core

I am beginner to integrate razorpay payment gateway on our angular and asp.net core website. Getting 500 error while posting the data to gateway url. Please check my code and pour your answers. Ia m searching it for nearly 2 days.
public async Task<IActionResult> CreateOrder([FromBody] TicketSales Sales)
{
System.Net.ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
string razorkey = "key";
string secret = "secret";
RazorpayClient client = new RazorpayClient(razorkey, secret);
Dictionary<string, object> options = new Dictionary<string, object>();
options.Add("amount", Sales.subTotal.Replace(".", "")); // amount in the smallest currency unit
options.Add("receipt", "Receipt_567");
options.Add("currency", "INR");
options.Add("payment_capture", "0");
Order order = client.Order.Create(options);
using (var httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri("https://api.razorpay.com/v1/checkout/embedded");
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("key",razorkey),
new KeyValuePair<string, string>("Amount", Sales.subTotal),
new KeyValuePair<string, string>("currency", "INR"),
new KeyValuePair<string, string>("name",Sales.bName),
new KeyValuePair<string, string>("description","Test Transaction"),
new KeyValuePair<string, string>("imag", ""),
new KeyValuePair<string, string>("order_id",Convert.ToString(order["id"])),
new KeyValuePair<string, string>("callback_url","localhost:4200//signin"),
});
var result = await httpClient.PostAsync("https://api.razorpay.com/v1/checkout/embedded", content);
if (result.IsSuccessStatusCode)
{
}
}
return Json(new { orderId = order["id"].ToString(),result });
}
check razorpay .net reference here
you have to post the error then someone may give you the solution!
For JavaScript client, you should consider the following flow while using asp.net core,
I have used it with the React.js client but you can find some similarities and make it work for the Angular.
This is the official documentation link for javascript client integration with your backend server,
https://razorpay.com/docs/payment-gateway/web-integration/standard/#step-1-create-an-order-from-your-server
This is my React.js client app handler which will be called on button click,
<script src="https://checkout.razorpay.com/v1/checkout.js"></script>
const handleRazorpayPayment = () => {
const data = {}; // here anything extra can be passed while creating an order
const response = await axios.post(`api/payment/initialize`, data);
const order_id = response.data.id;
const options = {
key: `razorpay_key`,
amount: 200,
name: 'Your javascript client app',
description: 'Pro Membership',
image: '/your_logo.png',
order_id: order_id,
handler: (response) => {
axios.post(`api/payment/confirm`, response)
.then(response=>alert(response.data))
.catch((err)=>console.log(err))
},
prefill: {
name: "TESTUSER",
email: "testuser#mail.com",
},
theme: {
color: '#F37254'
}
};
const rzp1 = new window.Razorpay(options);
rzp1.open();
};
This is the PaymentController.cs which will create an Order using Razorpay client library,
[Route("api/[controller]")]
[ApiController]
public class PaymentController : ControllerBase
{
private RazorpayClient _razorpayClient;
public PaymentController()
{
_razorpayClient = new RazorpayClient("key", "secret");
}
[HttpPost]
[Route("initialize")]
public async Task<IActionResult> InitializePayment()
{
var options = new Dictionary<string, object>
{
{ "amount", 200 },
{ "currency", "INR" },
{ "receipt", "recipt_1" },
// auto capture payments rather than manual capture
// razor pay recommended option
{ "payment_capture", true }
};
var order = _razorpayClient.Order.Create(options);
var orderId = order["id"].ToString();
var orderJson = order.Attributes.ToString();
return Ok(orderJson);
}
public class ConfirmPaymentPayload
{
public string razorpay_payment_id { get; }
public string razorpay_order_id { get; }
public string razorpay_signature { get; }
}
[HttpPost]
[Route("confirm")]
public async Task<IActionResult> ConfirmPayment(ConfirmPaymentPayload confirmPayment)
{
var attributes = new Dictionary<string, string>
{
{ "razorpay_payment_id", confirmPayment.razorpay_payment_id },
{ "razorpay_order_id", confirmPayment.razorpay_order_id },
{ "razorpay_signature", confirmPayment.razorpay_signature }
};
try
{
Utils.verifyPaymentSignature(attributes);
// OR
var isValid = Utils.ValidatePaymentSignature(attributes);
if (isValid)
{
var order = _razorpayClient.Order.Fetch(confirmPayment.razorpay_order_id);
var payment = _razorpayClient.Payment.Fetch(confirmPayment.razorpay_payment_id);
if (payment["status"] == "captured")
{
return Ok("Payment Successful");
}
}
}
catch (Exception ex)
{
return StatusCode(StatusCodes.Status500InternalServerError);
}
return StatusCode(StatusCodes.Status500InternalServerError);
}
}

In Blazor client app after login top of home page says Hello + a very long hash code instead of user name

After every login in a Blazor client app I see below picture and I have to refresh the page so It replaces the hash code with user name or email
loginDisplay.razor is where it supposed to show the name/email of the user:
<AuthorizeView>
<Authorized>
Hello, #context.User.Identity.Name!
Log out
</Authorized>
<NotAuthorized>
Register
Log in
</NotAuthorized>
I used the custom state provider found from this article by Chris Sainty.
GetAuthentication searches in localStorage if there exists a saved token return a new state based on that otherwise creates a new.
MarkAsAuthenticated lets the state provider know that a user logged in with provided email
MarkAsLogedOut is the opposite of the above one.
ParseClaimsFromJwt is reading the claims from the token
:
public class ApiAuthenticationStateProvider : AuthenticationStateProvider
{
private readonly HttpClient _httpClient;
private readonly ILocalStorageService _localStorage;
public ApiAuthenticationStateProvider(HttpClient httpClient, ILocalStorageService localStorage)
{
_httpClient = httpClient;
_localStorage = localStorage;
}
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
{
var savedToken = await _localStorage.GetItemAsync<string>("authToken");
if (string.IsNullOrWhiteSpace(savedToken))
{
return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
}
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", savedToken);
return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity(ParseClaimsFromJwt(savedToken), "jwt")));
}
public void MarkUserAsAuthenticated(string email)
{
var authenticatedUser = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, email) }, "apiauth"));
var authState = Task.FromResult(new AuthenticationState(authenticatedUser));
NotifyAuthenticationStateChanged(authState);
}
public void MarkUserAsLoggedOut()
{
var anonymousUser = new ClaimsPrincipal(new ClaimsIdentity());
var authState = Task.FromResult(new AuthenticationState(anonymousUser));
NotifyAuthenticationStateChanged(authState);
}
private IEnumerable<Claim> ParseClaimsFromJwt(string jwt)
{
var claims = new List<Claim>();
var payload = jwt.Split('.')[1];
var jsonBytes = ParseBase64WithoutPadding(payload);
var keyValuePairs = JsonSerializer.Deserialize<Dictionary<string, object>>(jsonBytes);
keyValuePairs.TryGetValue(ClaimTypes.Role, out object roles);
if (roles != null)
{
if (roles.ToString().Trim().StartsWith("["))
{
var parsedRoles = JsonSerializer.Deserialize<string[]>(roles.ToString());
foreach (var parsedRole in parsedRoles)
{
claims.Add(new Claim(ClaimTypes.Role, parsedRole));
}
}
else
{
claims.Add(new Claim(ClaimTypes.Role, roles.ToString()));
}
keyValuePairs.Remove(ClaimTypes.Role);
}
claims.AddRange(keyValuePairs.Select(kvp => new Claim(kvp.Key, kvp.Value.ToString())));
return claims;
}
private byte[] ParseBase64WithoutPadding(string base64)
{
switch (base64.Length % 4)
{
case 2: base64 += "=="; break;
case 3: base64 += "="; break;
}
return Convert.FromBase64String(base64);
}
}
There is a small problem in the code of that article, just pass Email instead of token. Note the name of the parameter in void MarkUserAsAuthenticated(string email).
In AuthService.cs
((ApiA uthenticationStateProvider)_authenticationStateProvider)
//.MarkUserAsAuthenticated(loginResult.Token);
.MarkUserAsAuthenticated(loginModel.Email);

Google.Apis.Requests.RequestError Not Found when deleting event

I have below method to delete event in calendar:
public async Task<string> DeleteEventInCalendarAsync(TokenResponse token, string googleUserId, string calendarId, string eventId)
{
string result = null;
try
{
if (_calService == null)
{
_calService = GetCalService(token, googleUserId);
}
// Check if event exist
var eventResource = new EventsResource(_calService);
var erListRequest = eventResource.List(calendarId);
var eventsResponse = await erListRequest.ExecuteAsync().ConfigureAwait(false);
var existingEvent = eventsResponse.Items.FirstOrDefault(e => e.Id == eventId);
if (existingEvent != null)
{
var deleteRequest = new EventsResource.DeleteRequest(_calService, calendarId, eventId);
result = await deleteRequest.ExecuteAsync().ConfigureAwait(false);
}
}
catch (Exception exc)
{
result = null;
_logService.LogException(exc);
}
return result;
}
And I am getting error as follow -
Google.GoogleApiException Google.Apis.Requests.RequestError Not Found [404] Errors [ Message[Not Found] Location[ - ] Reason[notFound] Domain[global] ]
Can you help me understand why this error? Or where I can find the details about these error?
The error you are getting is due to the event's id you are passing doesn't exist or you are passing it in the wrong way. Following the .Net Quickstart I made a simple code example on how to pass the event's id to the Delete(string calendarId, string eventId) method from the Class Events
namespace CalendarQuickstart
{
class Program
{
// If modifying these scopes, delete your previously saved credentials
// at ~/.credentials/calendar-dotnet-quickstart.json
static string[] Scopes = { CalendarService.Scope.Calendar };
static string ApplicationName = "Google Calendar API .NET Quickstart";
static void Main(string[] args)
{
UserCredential credential;
using (var stream =
new FileStream("credentials.json", FileMode.Open, FileAccess.Read))
{
// The file token.json stores the user's access and refresh tokens, and is created
// automatically when the authorization flow completes for the first time.
string credPath = "token.json";
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
Console.WriteLine("Credential file saved to: " + credPath);
}
// Create Google Calendar API service.
var service = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
// Define request.
EventsResource.ListRequest request = service.Events.List("primary");
// List events.
Events events = request.Execute();
Event existingEvent = events.Items.FirstOrDefault(e => e.Id == "your event id you want to get");
Console.WriteLine("Upcoming events:");
if (existingEvent != null)
{
Console.WriteLine("{0} {1}", existingEvent.Summary, existingEvent.Id);
string deleteEvent = service.Events.Delete("primary", existingEvent.Id).Execute();
}
else
{
Console.WriteLine("No upcoming events found.");
}
Console.Read();
}
}
}
Notice
I made this example in a synchronous syntax way for testing purposes in the console. After you test it and see how it works, you could adapt it to your code. Remember, make your you are passing the correct Id.
Docs
For more info check this doc:
Namespace Google.Apis.Calendar.v3

IdentityServer3: Some Claims not being returned from identity server

Context:
I am using ASP.NET MVC with OWIN self host. Below are the rest of the configs/setup.
In my Clients in identity server (notice the AllowedScopes set):
public static class InMemoryClientSource
{
public static List<Client> GetClientList()
{
return new List<Client>()
{
new Client()
{
ClientName = "Admin website",
ClientId = "admin",
Enabled = true,
Flow = Flows.Hybrid,
ClientSecrets = new List<Secret>()
{
new Secret("admin".Sha256())
},
RedirectUris = new List<string>()
{
"https://admin.localhost.com/"
},
PostLogoutRedirectUris = new List<string>()
{
"https://admin.localhost.com/"
},
AllowedScopes = new List<string> {
Constants.StandardScopes.OpenId,
Constants.StandardScopes.Profile,
Constants.StandardScopes.Email,
Constants.StandardScopes.Roles
}
}
};
}
}
Here are the Scopes:
public static class InMemoryScopeSource
{
public static List<Scope> GetScopeList()
{
var scopes = new List<Scope>();
scopes.Add(StandardScopes.OpenId);
scopes.Add(StandardScopes.Profile);
scopes.Add(StandardScopes.Email);
scopes.Add(StandardScopes.Roles);
return scopes.ToList();
}
}
In the Identity Server, here's how the server is configured. (Notice the Clients and Scopes are the ones provided above) :
var userService = new UsersService( .... repository passed here .... );
var factory = new IdentityServerServiceFactory()
.UseInMemoryClients(InMemoryClientSource.GetClientList())
.UseInMemoryScopes(InMemoryScopeSource.GetScopeList());
factory.UserService = new Registration<IUserService>(resolver => userService);
var options = new IdentityServerOptions()
{
Factory = factory,
SigningCertificate = Certificates.Load(), // certificates blah blah
SiteName = "Identity"
};
app.UseIdentityServer(options);
Finally, on the client web application side, this is how auth is set up:
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationType = "Cookies"
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions()
{
Authority = "https://id.localhost.com",
ClientId = "admin",
RedirectUri = "https://admin.localhost.com/",
PostLogoutRedirectUri = "https://admin.localhost.com/",
ResponseType = "code id_token token",
Scope = "openid profile email roles",
ClientSecret = "admin",
SignInAsAuthenticationType = "Cookies"
});
I have implemented a custom class for IUserService:
public class UsersService : UserServiceBase
{
public UsersService( .... repository passed here .... )
{
//.... ctor stuff
}
public override Task AuthenticateLocalAsync(LocalAuthenticationContext context)
{
// var user = .... retrieved from database .....
// ... auth logic ...
if (isAuthenticated)
{
var claims = new List<Claim>();
claims.Add(new Claim(Constants.ClaimTypes.GivenName, user.FirstName));
claims.Add(new Claim(Constants.ClaimTypes.FamilyName, user.LastName));
claims.Add(new Claim(Constants.ClaimTypes.Email, user.EmailAddress));
context.AuthenticateResult = new AuthenticateResult(user.Id.ToString(), user.EmailAddress, claims);
}
return Task.FromResult(0);
}
}
As you see, the claims are passed in this line:
context.AuthenticateResult = new AuthenticateResult(user.Id.ToString(), user.EmailAddress, claims);
When I try logging in to IdentityServer3, I can log in successfully to the client web application. HOWEVER, when I get the user claims, I don't see any identity claims. No given_name, family_name, and email claims. Screenshot below:
Anything I might have missed? Thanks in advance!
My solution was to add a list of claims to my scope configuration in order to return those claims. The wiki's documentation here described it.
For an in-memory client all I did was something like this:
public class Scopes
{
public static IEnumerable<Scope> Get()
{
return new Scope[]
{
StandardScopes.OpenId,
StandardScopes.Profile,
StandardScopes.Email,
StandardScopes.Roles,
StandardScopes.OfflineAccess,
new Scope
{
Name = "yourScopeNameHere",
DisplayName = "A Nice Display Name",
Type = ScopeType.Identity,
Emphasize = false,
Claims = new List<ScopeClaim>
{
new ScopeClaim("yourClaimNameHere", true),
new ScopeClaim("anotherClaimNameHere", true)
}
}
};
}
}
Finally found the solution for this problem.
First, I moved the creation of claims to the overridden GetProfileDataAsync (in my UserService class). Here's my implementation of it:
public override Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var identity = new ClaimsIdentity();
UserInfo user = null;
if (!string.IsNullOrEmpty(context.Subject.Identity.Name))
user = _facade.Get(context.Subject.Identity.Name);
else
{
// get the sub claim
var claim = context.Subject.FindFirst(item => item.Type == "sub");
if (claim != null)
{
Guid userId = new Guid(claim.Value);
user = _facade.Get(userId);
}
}
if (user != null)
{
identity.AddClaims(new[]
{
new Claim(Constants.ClaimTypes.PreferredUserName, user.Username),
new Claim(Constants.ClaimTypes.Email, user.EmailAddress)
// .. other claims
});
}
context.IssuedClaims = identity.Claims; //<- MAKE SURE you add the claims here
return Task.FromResult(identity.Claims);
}
Make sure that we pass the claims to the "context.IssueClaims" inside the GetProfileDataAsync() before returning the task.
And for those interested on how my AuthenticateLocalAsync() looks like:
var user = _facade.Get(context.UserName);
if (user == null)
return Task.FromResult(0);
var isPasswordCorrect = BCrypt.Net.BCrypt.Verify(context.Password, user.Password);
if (isPasswordCorrect)
{
context.AuthenticateResult = new AuthenticateResult(user.Id.ToString(), user.Username);
}
return Task.FromResult(0);
I raised a similar issue in IdentityServer3 GitHub project page that contains the explanation on why I encountered my issue. Here's the link:
https://github.com/IdentityServer/IdentityServer3/issues/1938
I am not using the identity server, however I am using the Windows Identity Foundation, which I believe is what IdentityServer uses. In order to access the claims I use:
((ClaimsIdentity)User.Identity).Claims

Pass a variable from a Custom Filter to controller action method

I have a Web Api project.
I have implemented a custom Authentication Attribute like so:
public class TokenAuthenticationAttribute : System.Web.Http.Filters.ActionFilterAttribute
{
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
// In auth web method you should implement functionality of authentication
// so that client app could be able to get token
if (actionContext.Request.RequestUri.AbsolutePath.Contains("api/auth/login"))
{
return;
}
// Receive token from the client. Here is the example when token is in header:
var token = HttpContext.Current.Request.Headers["Token"];
// Put your secret key into the configuration
var secretKey = ConfigurationManager.AppSettings["JWTSecurityKey"];
try
{
string jsonPayload = JWT.JsonWebToken.Decode(token, secretKey);
int separatorIndex = jsonPayload.IndexOf(';');
string userId = "";
DateTime timeIssued = DateTime.MinValue;
if (separatorIndex >= 0)
{
//userId = UTF8Encoding.UTF8.GetString(Convert.FromBase64String(jsonPayload.Substring(0, separatorIndex)));
userId = jsonPayload.Substring(0, separatorIndex);
timeIssued = DateTime.Parse(jsonPayload.Substring(separatorIndex + 1));
}
short TokenTTL = 10;
//try{
//Int16.TryParse(ConfigurationManager.AppSettings["TokenTTL"],TokenTTL);
//}catch(Exception e){ //}
if ((DateTime.Now.Subtract(timeIssued).TotalMinutes >= TokenTTL))
{
throw new HttpResponseException(HttpStatusCode.Forbidden);
}
//Save user in context
var claims = new List<Claim>()
{
new Claim(ClaimTypes.Name, userId)
};
var id = new ClaimsIdentity(claims, "Basic");
var principal = new ClaimsPrincipal(new[] { id });
actionContext.Request.GetRequestContext().Principal = principal;
}
catch (JWT.SignatureVerificationException)
{
throw new HttpResponseException(HttpStatusCode.Unauthorized);
}
}
}
Now how do I get hold of that user in my actionmethod?
[BasicHttpAuthorizeAttribute]
[httpGet]
public void Login()
{
// how do i get user here
}
/////// Save the string username to the context so that I can acess
it in the controler.
var claims = new List<Claim>()
{
new Claim(ClaimTypes.Name, "john")
};
var id = new ClaimsIdentity(claims, "Basic");
var principal = new ClaimsPrincipal(new[] { id });
actionContext.Request.GetRequestContext().Principal = principal;
// how do i get user here
var name = User.Identity.Name;
BTW, use an authentication filter instead of an authorization filter to perform authentication. See my blog post - http://lbadri.wordpress.com/2014/02/13/basic-authentication-with-asp-net-web-api-using-authentication-filter/.

Resources