I am building a web API that will serve as a connector between a 3rd-party application and mine.
This application will be running on a server and will be receiving POST requests from the 3rd-party application and sending POST requests of its own as a response.
Before it starts sending these requests, my web API needs to make a POST to the 3rd-party service, so it can be registered and received an authorization token, that it will be used on the requests it sends back, kinda similar to an OAuth token, from what I understand.
Since my code is all inside an HttpPost method, it only gets activated when it receives a call, and that part work as expected. When the service is authenticated and is receiving requests, is fine. The problem is when my service or the 3rd-party is restarted or something, the current token is made invalid or lost and a new one needs to be requested again.
What I wish to do is make that the call to register my service and receive the token is sent when the service starts, automatically.
Currently I am doing a manual call to trigger when my service needs to be registered, but that make it necessary for me to be at my computer to do so, and the connection is not make until I call that request.
Here is a sample of my code:
public class Controller : ApiController
{
static string SessionToken = "";
[HttpPost]
[Route("connector/webhook")]
public async Task<HttpStatusCode> Webhook(UpdateContentRequestBody body)
{
var NO_ERROR = 0;
try
{
if (string.IsNullOrEmpty(SessionToken))
{
// This registers my service.
var registerConector = ConectorOSCCApi.RegisterConector();
if (respostaRegistrarConector.ErrorCode != NO_ERROR)
{
throw new Exception();
}
SessionToken = registerConector.SessionToken;
}
ConectorApi.KeepAliveRequest(SessionToken);
RepeatKeepAlive();
ProccessDataAndSendResponseRequest(body);
return HttpStatusCode.OK;
}
catch (Exception e)
{
SessionToken = "";
return HttpStatusCode.InternalServerError;
}
I want the method to register the service to run without the need of a call to "connector/webhook", but the rest of the processing and response to only happens when such a call is received. How can I do that?
EDIT:
My code is inside a ASP.NET Web Application.
I am using .NET Framework 4.5 and hosting my web application on IIS.
This should do it for you :)
public class Controller : ApiController
{
static string _sessionToken = "";
static string SessionToken
{
get
{
if (string.IsNullOrEmpty(_sessionToken))
{
InitToken();
}
return _sessionToken
}
}
void InitToken()
{
if (string.IsNullOrEmpty(_sessionToken))
{
// This registers my service.
var registerConector = ConectorOSCCApi.RegisterConector();
if (respostaRegistrarConector.ErrorCode != NO_ERROR)
{
throw new Exception();
}
_sessionToken = registerConector.SessionToken;
}
}
public Controller() : base()
{
InitToken();
// anything else
}
[HttpPost]
[Route("connector/webhook")]
public async Task<HttpStatusCode> Webhook(UpdateContentRequestBody body)
{
var NO_ERROR = 0;
try
{
ConectorApi.KeepAliveRequest(SessionToken);
RepeatKeepAlive();
ProccessDataAndSendResponseRequest(body);
return HttpStatusCode.OK;
}
catch (Exception e)
{
SessionToken = "";
return HttpStatusCode.InternalServerError;
}
}
}
You don't need to wait for a request to your service to request a token.
Prerequisites : make sure you know what error code you receive from the third party API if your token is no longer correct.
When your API initializes, you will have a method available, ApplicationStart or something else in Startup.cs, depending on version, setup etc. Use that method to request the token from the third party API. Cache the token in the application level cache.
An example of caching can be found here: Caching Data in Web API
When your application receives a request, grab the token from the cache and issue the call to the third part API. If everything works, happy days. If it fails with token issue error code, then re-issue the token request and try again this time with the fresh token. Replace the cached token with the new one.
So basically, keep using a token until it fails, then automatically request a new one and update it. This way you don't need to be there to request the token manually.
You could wrap up this token logic into a service class so you don't have a lot to do in the endpoints.
Related
I have implemented some front end code which when a user clicks the checkout button they are redirected to a stripe page where they can input their card payment details. the code has a successful URL and failed URL. if the customer enter valid payment details - they are redirected to the successful URL, i need to update my database to ensure that my backend knows that this specific user has paid and can now view subscribed content. I am trying to setup web hooks in order to do this, so I know if the user has paid, cancelled etc.
using System;
using System.IO;
using Microsoft.AspNetCore.Mvc;
using Stripe;
namespace workspace.Controllers
{
[Route("api/[controller]")]
public class StripeWebHook : Controller
{
// You can find your endpoint's secret in your webhook settings
const string secret = "whsec_...";
[HttpPost]
public async Task<IActionResult> Index()
{
var json = await new StreamReader(HttpContext.Request.Body).ReadToEndAsync();
try
{
var stripeEvent = EventUtility.ConstructEvent(json,
Request.Headers["Stripe-Signature"], secret);
// Handle the checkout.session.completed event
if (stripeEvent.Type == Events.CheckoutSessionCompleted)
{
var session = stripeEvent.Data.Object as Checkout.Session;
// Fulfill the purchase...
HandleCheckoutSession(session);
}
else
{
return Ok()
}
}
catch (StripeException e)
{
return BadRequest();
}
}
}
}
However when trying to implement this I get errors because I think the custom code provided above uses .NET Core and I am using the full .NET framework.
Is there a way around this or what am I doing wrong?
This may help someone so I'm posting even although it's a bit late to the table as I couldn't find a relevant answer anywhere.
I had this same issue on a dotNet Core MVC web application (so not an exact answer for the question which is .Net Framework) where the Stripe Webhook was constantly giving a 400 Bad Request response. I just couldn't hit it no matter what I tried.
Eventually, and probably obviously the solution for me was to add the [IgnoreAntiforgeryToken] attribute to the Index() method as you have in your question above. As .dotNet Core enables the Validation Token on forms I had to explicitly ignore it. The Webhooks worked as soon as I did that.
So the solution for me was:
[HttpPost]
[IgnoreAntiforgeryToken]
public async Task<IActionResult> Index()
This apparently applies to dot Net Core versions: see Microsofts Documentation
Hope this helps someone.
That's works in my Asp.net Framework 4.7, try below code for the webhook
[HttpPost]
[Route("api/[controller]/webhook")]
public async Task<HttpResponseMessage> ProcessRequest()
{
var json = await new StreamReader(HttpContext.Current.Request.InputStream).ReadToEndAsync();
try
{
var stripeEvent = EventUtility.ParseEvent(json);
// Handle the event
if (stripeEvent.Type == Events.PaymentIntentSucceeded)
{
var paymentIntent = stripeEvent.Data.Object as PaymentIntent;
// Then define and call a method to handle the successful payment intent.
// handlePaymentIntentSucceeded(paymentIntent);
}
else if (stripeEvent.Type == Events.PaymentMethodAttached)
{
var paymentMethod = stripeEvent.Data.Object as PaymentMethod;
// Then define and call a method to handle the successful attachment of a PaymentMethod.
// handlePaymentMethodAttached(paymentMethod);
}
// ... handle other event types
else
{
// Unexpected event type
Console.WriteLine("Unhandled event type: {0}", stripeEvent.Type);
}
return Request.CreateResponse(HttpStatusCode.OK);
}
catch (StripeException e)
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
//Modification and Saving Data
}
After adding this webhook , you can test on locally from https://stripe.com/docs/webhooks/test this link
I'm trying to integrate a Microsoft account login into my ASP.NET MVC app, and I have this controller method:
public void SignIn()
{
// HACK - we will be signed into only one account if we are not signed in to MS
if (Request.GetOwinContext().Authentication.User.Identities.Count() <= 1)
{
// Signal OWIN to send an authorization request to Azure
Request.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties { RedirectUri = "http://localhost:31503/MicrosoftCalendar" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
What I expect to happen is that I'm prompted to log in to my Microsoft account; instead, what happens is this method runs over and over and over again, doing nothing at all, until I get a "too many redirects" error in my browser. How can I get the Challenge method to actually do something?
I have a class OwinStartup in my web project; I have it set to be the OWIN startup class like so:
[assembly: OwinStartup(typeof(Root.OwinStartup))]
However for some reason my breakpoints inside this startup class never get hit; OWIN is never being initialized... actually, wait a second, it is being initialized, but the event handlers for things like OnAuthorizationCodeReceivedAsync are never being hit...
If I step through the code, after Challenge is called in the SignIn method, I get redirected for some reason to a UserController, which in turn redirects me back to the SignIn method. I wonder why I'm winding up in the UserController?
edit: I need more code? all right, this method in Global.asax.cs executes immediately after the OWIN calls:
protected void MvcApplication_BeginRequest(object sender, EventArgs e)
{
#region Set the context GUID cookie
if (null == Request.Cookies[CookieName.ContextGUID])
{
Response.SetCookie(new System.Web.HttpCookie(CookieName.ContextGUID, Guid.NewGuid().ToString()));
}
#endregion
// check to see whether SSL is required
if (System.Web.Security.FormsAuthentication.RequireSSL)
{
// check where the request is originating from
if (Request.UserHostName != "127.0.0.1" && Request.UserHostName != "localhost")
{
// check if the request is secure
if (!Request.IsSecureConnection)
{
string url = null;
// check for querystring segments
if (!String.IsNullOrEmpty(Request.ServerVariables["QUERY_STRING"]))
{
url = String.Format("https://{0}{1}?{2}",
Request.ServerVariables["SERVER_NAME"],
Request.ServerVariables["SCRIPT_NAME"],
Request.ServerVariables["QUERY_STRING"]);
}
else
{
url = String.Format("https://{0}{1}", Request.ServerVariables["SERVER_NAME"], Request.ServerVariables["SCRIPT_NAME"]);
}
// redirect to the secure url
Response.Redirect(url);
}
}
}
// verify the request
if (null != Request)
{
// NOTE: This is a workaround for the following exception thrown by the ReportViewer control when
// using a non-IE browser:
// Missing URL parameter: IterationId
// See the following reference: https://connect.microsoft.com/VisualStudio/feedback/details/556989/?wa=wsignin1.0
if (Request.Path.EndsWith("Reserved.ReportViewerWebControl.axd") &&
Request.QueryString["ResourceStreamID"] != null &&
Request.QueryString["ResourceStreamID"].ToLower().Contains("blank.gif"))
{
// intercept the request and send to actual valid image path
Response.Redirect(Constant.ImageRoot + "blank.gif");
}
}
}
Not sure if this is what's causing the infinite redirect loop but here it is...
This is maybe a shot in the dark, but it looks like the controller isn't returning anything because it is a void method, try adding a return type, I'm not overly familier with OWIN so you'll have to forgive me there but here is an example of what I'm talking about:
public ActionResult SignIn()
{
// Signal OWIN to send an authorization request to Azure
return Request.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = "http://localhost:31503/MicrosoftCalendar" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
two small changes which are in the method signature, returning ActionResult and not a void, you may have to do a bit of research here on the class that OWIN actually returns. and second adding the return keyword, note this will not work with the if statement that you have said is a "hack" because you would require two return statements in that scenario
Hope this helps.
I have the following Invoke function within a middleware to log the web api requests. It is based on this example, and it works fine. However, I also want to save the username of the current user who is sending the request.
public async Task Invoke(HttpContext httpContext, IApiLogService apiLogService)
{
try
{
_apiLogService = apiLogService;
var request = httpContext.Request;
if (request.Path.StartsWithSegments(new PathString("/api")))
{
var stopWatch = Stopwatch.StartNew();
var requestTime = DateTime.UtcNow;
var requestBodyContent = await ReadRequestBody(request);
var originalBodyStream = httpContext.Response.Body;
await SafeLog(requestTime,
stopWatch.ElapsedMilliseconds,
200,//response.StatusCode,
request.Method,
request.Path,
request.QueryString.ToString(),
requestBodyContent
);
}
else
{
await _next(httpContext);
}
}
catch (Exception ex)
{
await _next(httpContext);
}
}
However, the User property of httpContext seems to missing user information. Below I share its content. I wonder how I can get the id of the user who is sending the request. I am not using windows authentication. Any suggestions?
The key point to explain why this happens
the User property of httpContext seems to missing user information
Is the order of middleware in the Configure method of the Startup class.
The ASP.NET Core request pipeline processing works by running middleware components as per the sequence they are placed in the Configure method of the Startup class.
so you needs to move app.UseAuthentication(); to be before you add your custom middleware to pipline app.UseYourMiddleware(); in the Configure method of the Startup class.
I have .NET Web API Project for the fulfillment API as our webhook in my Dialogflow agent. In our Post method of the controller, after getting the request from Dialogflow, I implement the explicit authentication as shown in the Google Cloud documentation for C#.
//jsonFileName is the name of the serviceAccountKey json generated from the Google Cloud Platform that's encrypted internally
public bool AuthExplicit(string projectId, string jsonFileName)
{
try
{
string JsonCredential = DecryptHelper.Decrypt(jsonFileName);
var credential = GoogleCredential.FromJson(JsonCredential).CreateScoped(LanguageServiceClient.DefaultScopes);
var channel = new Grpc.Core.Channel(
LanguageServiceClient.DefaultEndpoint.ToString(),
credential.ToChannelCredentials());
var client = LanguageServiceClient.Create(channel);
AnalyzeSentiment(client);
if (client != null)
{
return true;
}
else
{
return false;
}
}
internal void AnalyzeSentiment(LanguageServiceClient client)
{
var response = client.AnalyzeSentiment(new Document()
{
Content = "Authenticated.",
Type = Document.Types.Type.PlainText
});
var sentiment = response.DocumentSentiment;
string score = $"Score: {sentiment.Score}";
string magnitude = $"Magnitude: {sentiment.Magnitude}";
}
The difference with the code is that after getting the client, when we call the AnalyzeSentiment() method, it doesn't do anything, and the projectId parameter is never used to authenticate. GCP docs are quite confusing, since when there is an AuthExplicit() that uses projectId, it uses it as a parameter for the buckets and only prints this on the console.
It works fine, until we test the service account key with a different agent. Expected output is that authentication would fail, but somehow it still passes.
Once the Post method goes through the AuthExplicit() method, it would only return a boolean. Is this the right way to authenticate? Or is there something else needed to invoke?
The difference with the code is that after getting the client, when we call the AnalyzeSentiment() method, it doesn't do anything,
Does client.AnalyzeSentiment() return an empty response? Does the call hang forever?
It works fine, until we test the service account key with a different agent.
What is a different agent? A different User-Agent header?
Once the Post method goes through the AuthExplicit() method, it would only return a boolean. Is this the right way to authenticate? Or is there something else needed to invoke?
What does 'the Post method' refer to? What is the 'it' that would only return a boolean?
I am working on a cross platform web app using angular and webapi. The problem is when the angular app runs in a cordova container. To play nice with the rest of the applications on the device, I am required to use a plugin for SSO.. This plugin is what is causing me issues, because it does a few things. It intercepts all the http requests and adds a bearer token to the header, which is generated by a 3rd party Token provider, so I can't decode it, and overwrites any bearer token I have set in the header.It also seems to block cookies..
So it makes it a bit tricky when you can't send you own local credentials.
So I started with https://coding.abel.nu/2014/06/writing-an-owin-authentication-middleware/ and http://katanaproject.codeplex.com/SourceControl/latest#src/Microsoft.Owin.Security.OAuth/OAuthBearerAuthenticationHandler.cs
So I figured I should write my own middleware to take care of this; I thought since the standard oauth middleware can work without cookies, I should not have too hard a time getting my slightly different bearer token middleware to do it.. But that has not been the case... Writing my own middleware.. so I'm able to get the header, validate with the external token provider, but I can't actually sign in.
protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
{
try
{
// Find token in default location
string requestToken = null;
string authorization = Request.Headers.Get("Authorization");
if (!string.IsNullOrEmpty(authorization))
{
if (authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
{
requestToken = authorization.Substring("Bearer ".Length).Trim();
}
}
.... Take the Request token call other Server, verify token...
Also
public override async Task<bool> InvokeAsync()
{
var ticket = await this.AuthenticateAsync();
if(ticket != null)
{
this.Context.Authentication.SignIn(new AuthenticationProperties(), grantIdentity);
return false;
}
}
So in the end the SignIn does not cause a error or anything, but does not actually signin. As soon as I get to a controller action with an [Authorize] attribute, I get a 401. I not have any external cookies enabled. There is a high probability that I am on the wrong track or I am making it way too hard.
You are doing it way too hard.
Instead of creating your own bearer authentication middleware you should change the default OAuthBearerAuthenticationProvider.
Here is a sample for sending the token in the query string.
//in Startup class
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
{
Provider = new QueryStringOAuthBearerProvider(),
//your settings
});
//implementation
public class QueryStringOAuthBearerProvider : OAuthBearerAuthenticationProvider
{
private const string AccessTokenQueryKey = "access_token";
public override Task RequestToken(OAuthRequestTokenContext context)
{
//check if token found in the default location - "Authorization: Bearer <token>" header
if (string.IsNullOrEmpty(context.Token))
{
var token = context.Request.Query.Get(AccessTokenQueryKey);
if (!string.IsNullOrEmpty(token))
{
context.Token = token;
}
}
return Task.FromResult<object>(null);
}
}
So… I ment to answer it earlier, but I was able to figure it out, without override the authorize attribute. I ended up looking at the source for the OWIN security code. The trick is, you really need 2 OWIN middleware components. One is the what I call (and I stole this from the owin source) the server middleware. The server middleware responds to the challenge and/or if you are feeling crazy generate local credentials for you. This middleware is also a PASSIVE middleware component. I won’t get in to generating the local credentials unless someone asks , because it’s a bit off point, but if someone thinks it will be helpful, I can update.
public class LowCalorieAuthenticationServerHandler : AuthenticationHandler<LowCalorieAuthenticationServerOptions>
{
//Important this needs to be overriden, but just calls the base.
protected override Task<AuthenticationTicket> AuthenticateCoreAsync()
{
return Task.FromResult<AuthenticationTicket>(null);
}
/// <summary>The apply response challenge async.</summary>
/// <returns>The <see cref="Task"/>.</returns>
protected override async Task ApplyResponseChallengeAsync()
{
if (this.Response.StatusCode != 401)
{
Task.FromResult<object>(null);
return;
}
var challenge = this.Helper.LookupChallenge(
this.Options.AuthenticationType,
this.Options.AuthenticationMode);
if (challenge != null)
{
//OK in here you call the rediret to the 3rd party
//return a redirect to some endpoint
}
Task.FromResult<object>(null);
return;
}
}
Anyway notice how the override AuthenticateCoreAsync() just returns
return Task.FromResult(null);
This is because we don’t want this middleware to modify the request. ApplyResponseChallengeAsync will wait for a Challenge and redirect you to the 3rd party login. IF you want to create a local token of some sort you would override the InvokeAsync method
The second middle ware you need is the token/external credentials validator. This will then authenticate the user somehow. In the case of the local bearer token that is built into the OWIN security, it simple deserializes the token and if it can, and the token is not expired it authenticates the user. So in the case that you want to verify the token with a 3rd part sso, such as google or anything, you insert you logic here. In my case I not only wanted to call the 3rd party provider to get the user info, but to check if they token was still valid for single sign out, and to prevent multiple sessions.
public class LowCalorieAuthenticationHandler : AuthenticationHandler<LowCalorieAuthenticationOptions>
{
//Going to give you the user for the request.. You Need to do 3 things here
//1. Get the user claim from teh request somehow, either froma header, request string, or cookie what ever you want
//2. validate the user with whatever user store or 3rd party SSO you want
//3. Generate a AuthenticationTicket to send to on to the request, you can use that to see if the user is valid in any Identity collection you want.
protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
{
//Good to throw in a point of override here.. but to keep it simple-ish
string requestToken = null;
string authorization = Request.Headers.Get("Authorization");
//TOTAL FAKEOUT.. I am going to add a bearer token just so the simple sample works, but your client would have to provide this
authorization = "Bearer 1234567869";
//STEP 1
if (!string.IsNullOrEmpty(authorization) && authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
{
requestToken = authorization.Substring("Bearer ".Length).Trim();
return await FakeExternalBearer(requestToken);
}
return null;
}
private async Task<AuthenticationTicket> FakeExternalBearer(string token)
{
var authenticationType = Options.AuthenticationType;
//pretend to call extenal Resource server to get user //STEP 2
//CallExternal(token)
//Create the AuthTicket from the return.. I will fake it out
var identity = new ClaimsIdentity(
authenticationType,
ClaimsIdentity.DefaultNameClaimType,
ClaimsIdentity.DefaultRoleClaimType);
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier,"user1", null, authenticationType));
identity.AddClaim(new Claim(ClaimTypes.Name, "Jon",null, authenticationType));
var properties = new AuthenticationProperties();
properties.ExpiresUtc = DateTime.UtcNow.AddMinutes(1);
properties.IssuedUtc = DateTime.UtcNow;
var ticket = new AuthenticationTicket(identity, properties);
return ticket;
}
}
Ok here we override AuthenticateCoreAsync, but we actually do something now. This this were your do you user authentication. This is the ACTIVE part of the middleware. Note it needs to return a valid AuthenticationTicket. This will run on each request so be careful what you call and how often.
So I have a very simple example here https://github.com/jzoss/LowCalorieOwin If anyone is interested in more detail, please ask. I can add more. I did make it too hard, because now that I understand it, it’s pretty easy, but there is really no good examples on how to do this.