I am trying to get notifications form stripe about the events occured. This is the webhook endpoint which I used. The URL must be publicly accessed. When consider security, is this enough or should I use any other approach?
[HttpPost("WebHook/{id}")]
public async Task<IActionResult> WebHook(int id)
{
var json = await new StreamReader(HttpContext.Request.Body).ReadToEndAsync();
Event stripeEvent;
try
{
//Get webhook secret
string webHookSecret = XXXX;
//Construct stripe Event
stripeEvent = EventUtility.ConstructEvent(
json,
Request.Headers["Stripe-Signature"],
webHookSecret
);
}
catch (Exception ex)
{
LoggingUtil.LogError(ex, ex.Message);
return BadRequest();
}
}
The webhook signature verification alone is not enough, if only because Stripe uses message authentication with a shared secret rather than asymmetric cryptography digital signatures. If you don't have the paranoid intention of building a bulletproof server, you can start with Stripe's security recommendations. Trivial IP address filtering can make your URL not so "publicly accessible".
Related
I have this scenario: Xamarin.Forms App connected with Web Api 2. I make all requests and get the data i want. Now when the session token expires, i need to refresh the token but don't logout the user. The user don't need to know when token is refreshed. How to organize this, add in every request if statement when i send it and check if token expires.
This is one of my requests:
public async Task<User> GetProfileSetup()
{
try
{
if (CrossConnectivity.Current.IsConnected)
{
string token = DependencyService.Get<ISharedFunctions>().GetAccessToken();
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
var response = await client.GetAsync(#"api/Profile/GetProfilSetup");
if (response.IsSuccessStatusCode)
{
string jsonMessage;
using (Stream responseStream = await response.Content.ReadAsStreamAsync())
{
jsonMessage = new StreamReader(responseStream).ReadToEnd();
}
User user = JsonConvert.DeserializeObject<User>(jsonMessage);
return user;
}
else
{
var m = response.Content.ToString();
return null;
}
}
else
{
return null;
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
string error = ex.Message;
return null;
}
}
P.S I have Methods for GetToken and RefreshToken in my Api and they are working, just how to organize Refreshing ?
It really depends on what libraries are you using on your project.
But let's say you're using plain c# to handled your HTTP calls.
[OPTION 1] Polly
I can recommend you looking at Polly
It's a great library with a lot of features. You can use the Retry policy to handled expired tokens:
var _unauthorizedPolicy = Policy
.Handle<Exception>(ex => ex.StatusCode == HttpStatusCode.Unauthorized) // check here for your exception to be the right one
.RetryAsync(3, async (exception, retryCount, context) =>
{
try
{
var token = await _authService.RefreshToken();
// save the new token or whatever you need to store it
}
catch (Exception ex)
{
// RefreshToken failed, you should probably sign out the user
SignOut();
}
});
What this does is that Polly will try to execute your normal HTTP call and in case it fails and the cause is specified in Handle, then a retry mechanism is fired that will try to refresh the token and then retry your request. In the end, in case the token cannot be refreshed, you sign out the user. Of course, all this can be customized, check Polly's documentation is pretty well written.
Please note that inside Handle<T> you must put the right exception. I just used Exception as a placeholder since I'm not sure what Exception is thrown in your case.
Then you would call your method with this policy:
var result = await _unauthorizedPolicy.ExecuteAsync(() => GetProfileSetup())
And you can reuse that policy for any call, no need to create it every time.
[OPTION 2] DelegatingHandler
I will like here another StackOverflow answer:
How to Refresh a token using IHttpClientFactory
Basically you can intercept every HTTP call made via a HttpClient and refresh/add a token to your requests.
Note that that answer does not obligate you to use IHttpClientFactory, it also works for a simple HttpClient.
Also a little bit off-topic. You might want to look up for libraries to handle htt calls such as Retrofit. It will really reduce the amount of boilerplate code.
I am building back-end(API) of mobile application on asp.net core. Using swagger for visualization of API calls and so on. Currently I am creating external authentications, I have some problems with LinkedIn.
The structure of application is simple, mobile side gets LinkedIn user's Access Token and sends it to me with API request, I have to request user data from LinkedIn by received Access Token, register or login him/her and return response to mobile side.
There is the code below, and commented links where I tried to send requests.
public async Task<IResponse<LinkedInAuthenticationResponse>> LinkedInAuthentication(string accessToken)
{
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
httpClient.DefaultRequestHeaders.Add("x-li-format", "json");
var oauthUrl = "https://api.linkedin.com/v1/people";
// https://api.linkedin.com/v2/me
// https://api.linkedin.com/v1/people/~:(id,formatted-name,email-address,picture-url)
var response = httpClient.GetAsync(oauthUrl).Result;
string responseContent = string.Empty;
try
{
responseContent = await response.Content.ReadAsStringAsync();
response.EnsureSuccessStatusCode();
return Ok(JsonConvert.DeserializeObject<GoogleAuthenticationResponse>(responseContent));
}
catch (Exception ex)
{
}
}
}
The problem is that LinkedIn doesn't provide exact link where API call should be send, I had to try several of them but the response is always 401.
My Access Token is valid, this can not be problem. I have used almost same code for Facebook and Google and they work perfectly.
Any one can provide link which works for API calls ?
This issue has nothing to do with the asp.net framework but with the endpoint, you are using. Since v1 of the LinkedIn API has been deprecated, you need to change your endpoint as for this link here
Question
I have an identity server implementation that is being used by a number of applications in test and production. I am currently working on a new feature, where the client application using the identity server can perform Azure service management REST api calls. For this, it needs a token. I can generate this token, store it and even access it in the AccountController in the identity server.
My issue is figuring out how to send this to the client. I don't think this token belongs in the claims for the user. So I tried to add it as part of AuthenticationProperties as a token, but I cannot seem to access it in the client. Should I store it in a session like this SO user did link? There is one answer to this question, but that does not seem right (I even tried it out of desperation!)
Relevant sections of code
Generate the token
var resource = "https://management.azure.com/";
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
Events = new OpenIdConnectEvents
{
OnAuthorizationCodeReceived = async context =>
{
// Acquire the token for the resource and save it
}
}
}
Restore it in AccountController
public async Task<IActionResult> ExternalLoginCallback(string returnUrl)
{
string resource = "https://management.azure.com/";
// snip
result = await authContext.AcquireTokenSilentAsync(resource, credential, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));
// snip
AuthenticationProperties props = null;
var tokens = new List<AuthenticationToken>();
var id_token = info.Properties.GetTokenValue("id_token");
if (id_token != null)
{
tokens.Add(new AuthenticationToken { Name = "id_token", Value = id_token });
}
if (result != null)
{
tokens.Add(new AuthenticationToken { Name = "management_token", Value = result.AccessToken });
}
if (tokens.Any())
{
props = new AuthenticationProperties();
props.StoreTokens(tokens);
}
// snip
// Can I access these "props" on the client? I even tried adding it to `Items`, no luck.
await HttpContext.Authentication.SignInAsync(user.UserId, user.DisplayName, provider, props, additionalClaims.ToArray());
}
So, my question, is this the right way go about it? If so, how do I access the authentication properties set? Or should I try saving this in the Session? If so, how do I store it in the client's session?
Any pointers would help. Thank you!
Just wanted to post an answer so that people wanting the same can benefit.
A token cache can be implemented to achieve this. This repository explains how.
Pay special attention to the AdalDistributedTokenCache linked here
I am facing a rather simple situation but I cannot wrap my head around it. Maybe the OkHttp gurus could light my path.
I am using Picasso, Retrofit and OkHttp for multiple purposes on my android app. yay!. As I read properly, the developer should make an effort in keeping on OkHttpClient (as read here ).
With the approach in mind, I want any of my HTTP calls (be it an API call, an Image loading, a resource download) to :
Send the request
If HTTP401 is received, then Send another HTTP Request that sends a token back
When that token is received, the call is re-emitted with that token included in the headers
Any subsequent call (be it an API, a resource or image call) should use that token until the next HTTP401 is received (invalid token).
Of course, I would reuse the same client for Retrofit and Picasso.
One route I am considering is to use a mix of Authenticator and an application Interceptor. Authenticator should catch HTTP401, but can I make it make another sync request in the meantime, store the token and activate the new interceptor ?
Looks like I found the solution myself to that problem so let's share the knowledge to everyone.
In order to this, OkHttp already gives all the necessary hooks.
Make sure to use Authenticator
Install an interceptor once the authenticator succeed
Return a request with the good token.
This also imply that the Authenticator handles an HTTP to set your token back (done in another android service).
okHttpClient.setAuthenticator(new Authenticator() {
#Override
public Request authenticate(Proxy proxy, Response response) {
AccountManager accountManager = AccountManager.get(context);
Account[] accounts = accountManager.getAccountsByType(Authenticator.ACCOUNT_TYPE);
// No account, do not even try to authenticate
if (accounts.length == 0) {
Log.i(TAG, "... But we dont have any account yet, so I will just back off for now.");
return null;
}
Account account = accounts[0];
try {
final String mCurrentToken = accountManager.blockingGetAuthToken(account, "", false);
// For now, we just re-install blindly an interceptor
okHttpClient.interceptors().clear();
Log.i(TAG, "... Installing interceptor after authentication");
okHttpClient.interceptors().add(new Interceptor() {
#Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Request newReq = request.newBuilder()
.addHeader("Authorization", mCurrentToken)
.build();
Response response = chain.proceed(newReq);
return response;
}
});
Log.i(TAG, "Install temporary auth token in request");
return response.request().newBuilder()
.addHeader("Authorization", mCurrentToken)
.build();
} catch (OperationCanceledException e) {
Log.e(TAG, "Interrupted exception");
return null;
} catch (AuthenticatorException e) {
Log.e(TAG, "Authentication error");
return null;
} catch (IOException e) {
Log.e(TAG, "IO Error");
return null;
}
}
#Override
public Request authenticateProxy(Proxy proxy, Response response) {
return null; // Null indicates no attempt to authenticate.
}
})
With this, just use this OkClient in Picasso and Retrofit.
for example I have a web API : http://example.com/api/product.
I have a C# client to consume this web API. Something like that to get whole list of product.
// List all products.
HttpResponseMessage response = client.GetAsync("api/products").Result; // Blocking call!
if (response.IsSuccessStatusCode)
{
// Parse the response body. Blocking!
var products = response.Content.ReadAsAsync<IEnumerable<Product>>().Result;
foreach (var p in products)
{
Console.WriteLine("{0}\t{1};\t{2}", p.Name, p.Price, p.Category);
}
}
else
{
Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase);
}
How do I pass the username and password from C# client to server's API? What I want is when the C# client to get whole product list from web API.
The client will send the username and password to the server's API. if the server's web API checks whether it is authorized user from database, if not don't let it get product list.
I used the following approach in a proof of concept some time ago, I hope it helps you.
I wrote something like this, an "AuthenticationController" with 2 methods:
public bool Login(string username, string password, bool rememberMe)
{
if (Membership.ValidateUser(username, password))
{
FormsAuthentication.SetAuthCookie(username, rememberMe);
return true;
}
return false;
}
public void Logout()
{
FormsAuthentication.SignOut();
}
The Login method creates a cookie that will be sent to the client; then, in each request, you need to send it back to the server. You can use the [Authorize] attribute in your controller actions to validate allowed roles and rights.
My recommendation is to use have an authentication routine that will assign a token to the client. The client would then cache that token and pass that token in subsequent requests. The authentication routine should be via SSL to prevent sniffing on the wire and shouldn't be stored on the device at all (the token can be cached to the device).
This will give you a fair bit of control over the client. Your service is then in a position where it can preemptively deactivate the client (kill the token and force a re-auth - essentially a timemout situation). You are also in a position to protect your application on the client (if the application is compromised on the device the user credentials won't be passed around).
You could use DotNetOpenAuth to get you started along this path.
[System.Web.Mvc.AcceptVerbs(HttpVerbs.Post)]
public ActionResult LogOn(string loginIdentifier)
{
if (!Identifier.IsValid(loginIdentifier))
{
ModelState.AddModelError("loginIdentifier",
"The specified login identifier is invalid");
return View();
}
else
{
var openid = new OpenIdRelyingParty();
IAuthenticationRequest request = openid.CreateRequest(
Identifier.Parse(loginIdentifier));
// Require some additional data
request.AddExtension(new ClaimsRequest
{
BirthDate = DemandLevel.NoRequest,
Email = DemandLevel.Require,
FullName = DemandLevel.Require
});
return request.RedirectingResponse.AsActionResult();
}
}
Source: Sample Code