Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions OAuthDemo/Controllers/ChargeCreditCard.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ public static String Run(String AccessToken, String CardNumber, DateTime Expirat
{
Console.WriteLine("Charge Credit Card Sample");

ApiOperationBase<ANetApiRequest, ANetApiResponse>.RunEnvironment = AuthorizeNet.Environment.SANDBOX;

// define the merchant information (authentication / transaction id)
ApiOperationBase<ANetApiRequest, ANetApiResponse>.MerchantAuthentication = new merchantAuthenticationType()
// SECURITY: Authentication is set per-request on the request object (not via
// shared static properties) to prevent race conditions in multi-threaded
// ASP.NET environments. Environment is passed to Execute() for the same reason.
var merchantAuthentication = new merchantAuthenticationType()
{
ItemElementName = ItemChoiceType.accessToken,
Item = AccessToken,
Expand Down Expand Up @@ -58,11 +58,15 @@ public static String Run(String AccessToken, String CardNumber, DateTime Expirat
lineItems = lineItems
};

var request = new createTransactionRequest { transactionRequest = transactionRequest };
var request = new createTransactionRequest
{
merchantAuthentication = merchantAuthentication,
transactionRequest = transactionRequest
};

// instantiate the contoller that will call the service
// instantiate the controller that will call the service
var controller = new createTransactionController(request);
controller.Execute();
controller.Execute(AuthorizeNet.Environment.SANDBOX);

// get the response from the service (errors contained if any)
var response = controller.GetApiResponse();
Expand Down
111 changes: 80 additions & 31 deletions OAuthDemo/Controllers/DemoController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,51 @@

namespace OAuthDemo.Controllers
{
// This educational demo is intentionally usable without a login. To still prevent
// IDOR (one visitor tampering with a Demo record created by another visitor), each
// Demo is bound to the browser Session that created it, and every action validates
// that the requested Id belongs to the current session before touching it.
public class DemoController : Controller
{
private const string OwnedDemoIdsKey = "OwnedDemoIds";

private readonly ApplicationDbContext _context;
public DemoController()
{
_context = new ApplicationDbContext();
}

// The set of Demo Ids created by (and therefore owned by) the current session.
private HashSet<string> OwnedDemoIds
{
get
{
var owned = Session[OwnedDemoIdsKey] as HashSet<string>;
if (owned == null)
{
owned = new HashSet<string>();
Session[OwnedDemoIdsKey] = owned;
}
return owned;
}
}

// Returns the Demo only if it belongs to the current session; otherwise null,
// so callers can reject the request instead of acting on someone else's record.
private Demo GetOwnedDemo(string id)
{
if (string.IsNullOrEmpty(id) || !OwnedDemoIds.Contains(id))
return null;
return _context.Demos.SingleOrDefault(d => d.Id == id);
}

// GET: Demo
public ActionResult Index()
{
Demo DemoModel = new Demo(Guid.NewGuid().ToString());
_context.Demos.Add(DemoModel);
_context.SaveChanges();
OwnedDemoIds.Add(DemoModel.Id); // record session ownership for IDOR checks
return View(DemoModel);
}

Expand All @@ -33,30 +65,38 @@ public ActionResult RegisterApplication()
}

// step 2
public ActionResult RedirectMerchant(Demo InputModel)
public ActionResult RedirectMerchant(RedirectMerchantInput input)
{
System.Diagnostics.Debug.WriteLine(_context.Demos.ToString());
var SavedModel = _context.Demos.SingleOrDefault(d => d.Id == InputModel.Id);
SavedModel.ClientId = InputModel.ClientId;
SavedModel.RedirectUri = InputModel.RedirectUri;
SavedModel.Read = InputModel.Read;
SavedModel.Write = InputModel.Write;
SavedModel.State = InputModel.State;
SavedModel.Sub = InputModel.Sub;
var SavedModel = GetOwnedDemo(input.Id);
if (SavedModel == null)
return new HttpUnauthorizedResult();

SavedModel.ClientId = input.ClientId;
SavedModel.RedirectUri = input.RedirectUri;
SavedModel.Read = input.Read;
SavedModel.Write = input.Write;
SavedModel.State = input.State;
SavedModel.Sub = input.Sub;
SavedModel.updateRedirectMerchantUrl();

_context.SaveChanges();
return Redirect(SavedModel.RedirectMerchantUrl);
}

// step 3
public ActionResult RetrieveAccessToken(Demo InputModel)
public ActionResult RetrieveAccessToken(RetrieveAccessTokenInput input)
{
var SavedModel = _context.Demos.SingleOrDefault(d => d.Id == InputModel.Id);
SavedModel.GrantType = InputModel.GrantType;
SavedModel.Code = InputModel.Code;
SavedModel.ClientId = InputModel.ClientId;
SavedModel.ClientSecret = InputModel.ClientSecret;
var SavedModel = GetOwnedDemo(input.Id);
if (SavedModel == null)
return new HttpUnauthorizedResult();

SavedModel.GrantType = input.GrantType;
SavedModel.Code = input.Code;
SavedModel.ClientId = input.ClientId;
// WARNING: Demo application only - credentials stored in plaintext for educational purposes
// PRODUCTION CODE MUST encrypt credentials at rest using ASP.NET Data Protection API or column-level encryption
SavedModel.ClientSecret = input.ClientSecret;

try
{
Expand All @@ -76,13 +116,16 @@ public ActionResult RetrieveAccessToken(Demo InputModel)
}

// step 4
public ActionResult ChargeCreditCard(Demo InputModel)
public ActionResult ChargeCreditCard(ChargeCreditCardInput input)
{
var SavedModel = _context.Demos.SingleOrDefault(d => d.Id == InputModel.Id);
SavedModel.AccessToken = InputModel.AccessToken;
SavedModel.CardNumber = InputModel.CardNumber;
SavedModel.ExpirationDate = InputModel.ExpirationDate;
SavedModel.Amount = InputModel.Amount;
var SavedModel = GetOwnedDemo(input.Id);
if (SavedModel == null)
return new HttpUnauthorizedResult();

SavedModel.AccessToken = input.AccessToken;
SavedModel.CardNumber = input.CardNumber;
SavedModel.ExpirationDate = input.ExpirationDate;
SavedModel.Amount = input.Amount;

try
{
Expand All @@ -101,11 +144,14 @@ public ActionResult ChargeCreditCard(Demo InputModel)
return View("Index", SavedModel);
}

public ActionResult GetTransactionDetails(Demo InputModel)
public ActionResult GetTransactionDetails(GetTransactionDetailsInput input)
{
var SavedModel = _context.Demos.SingleOrDefault(d => d.Id == InputModel.Id);
SavedModel.AccessToken = InputModel.AccessToken;
SavedModel.TransactionId = InputModel.TransactionId;
var SavedModel = GetOwnedDemo(input.Id);
if (SavedModel == null)
return new HttpUnauthorizedResult();

SavedModel.AccessToken = input.AccessToken;
SavedModel.TransactionId = input.TransactionId;

try
{
Expand All @@ -124,13 +170,16 @@ public ActionResult GetTransactionDetails(Demo InputModel)
}

// step 5
public ActionResult RefreshAccessToken(Demo InputModel)
public ActionResult RefreshAccessToken(RefreshAccessTokenInput input)
{
var SavedModel = _context.Demos.SingleOrDefault(d => d.Id == InputModel.Id);
SavedModel.ClientId = InputModel.ClientId;
SavedModel.ClientSecret = InputModel.ClientSecret;
SavedModel.GrantType = InputModel.GrantType;
SavedModel.RefreshToken = InputModel.RefreshToken;
var SavedModel = GetOwnedDemo(input.Id);
if (SavedModel == null)
return new HttpUnauthorizedResult();

SavedModel.ClientId = input.ClientId;
SavedModel.ClientSecret = input.ClientSecret;
SavedModel.GrantType = input.GrantType;
SavedModel.RefreshToken = input.RefreshToken;

try
{
Expand All @@ -152,4 +201,4 @@ public ActionResult RedirectRevokePermissions()
return Redirect(Demo.RevokePermissionsUrl);
}
}
}
}
10 changes: 6 additions & 4 deletions OAuthDemo/Controllers/GetTransactionDetails.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,22 @@ public static String Run(String AccessToken, string transactionId)
{
Console.WriteLine("Get transaction details sample");

ApiOperationBase<ANetApiRequest, ANetApiResponse>.RunEnvironment = AuthorizeNet.Environment.SANDBOX;
// define the merchant information (authentication / transaction id)
ApiOperationBase<ANetApiRequest, ANetApiResponse>.MerchantAuthentication = new merchantAuthenticationType()
// SECURITY: Authentication is set per-request on the request object (not via
// shared static properties) to prevent race conditions in multi-threaded
// ASP.NET environments. Environment is passed to Execute() for the same reason.
var merchantAuthentication = new merchantAuthenticationType()
{
ItemElementName = ItemChoiceType.accessToken,
Item = AccessToken
};

var request = new getTransactionDetailsRequest();
request.merchantAuthentication = merchantAuthentication;
request.transId = transactionId;

// instantiate the controller that will call the service
var controller = new getTransactionDetailsController(request);
controller.Execute();
controller.Execute(AuthorizeNet.Environment.SANDBOX);

// get the response from the service (errors contained if any)
var response = controller.GetApiResponse();
Expand Down
54 changes: 54 additions & 0 deletions OAuthDemo/Models/DemoInputModels.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System;

namespace OAuthDemo.Models
{
// Input model for RedirectMerchant action
public class RedirectMerchantInput
{
public string Id { get; set; }
public string ClientId { get; set; }
public string RedirectUri { get; set; }
public bool Read { get; set; }
public bool Write { get; set; }
public string State { get; set; }
public string Sub { get; set; }
}

// Input model for RetrieveAccessToken action
public class RetrieveAccessTokenInput
{
public string Id { get; set; }
public string GrantType { get; set; }
public string Code { get; set; }
public string ClientId { get; set; }
public string ClientSecret { get; set; }
}

// Input model for ChargeCreditCard action
public class ChargeCreditCardInput
{
public string Id { get; set; }
public string AccessToken { get; set; }
public string CardNumber { get; set; }
public DateTime ExpirationDate { get; set; }
public decimal Amount { get; set; }
}

// Input model for GetTransactionDetails action
public class GetTransactionDetailsInput
{
public string Id { get; set; }
public string AccessToken { get; set; }
public string TransactionId { get; set; }
}

// Input model for RefreshAccessToken action
public class RefreshAccessTokenInput
{
public string Id { get; set; }
public string ClientId { get; set; }
public string ClientSecret { get; set; }
public string GrantType { get; set; }
public string RefreshToken { get; set; }
}
}
15 changes: 12 additions & 3 deletions OAuthDemo/Models/DemoModels.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@

namespace OAuthDemo.Models
{
// WARNING: This is a demonstration application for educational purposes only
// OAuth credentials are stored in plaintext in the database to simplify the demo
// PRODUCTION APPLICATIONS MUST encrypt sensitive data at rest using:
// - ASP.NET Data Protection API
// - SQL Server column-level encryption
// - Azure Key Vault or similar key management service
public class Demo
{
public const string RetrieveErrorResponse = "Error Retrieving the Access Token";
Expand All @@ -22,8 +28,11 @@ static Demo()

public Demo()
{
ClientId = "4dp5b7gRqk";
ClientSecret = "fa3a5b16753d09b24bb44243605a4a98";
// ClientId / ClientSecret must come from Web.config <appSettings> (use placeholders
// [YOUR_CLIENT_ID] / [YOUR_CLIENT_SECRET] until you fill in your own sandbox values).
// Obtain values from the Authorize.Net Sandbox portal — see README for the procedure.
ClientId = ConfigurationManager.AppSettings["ClientId"] ?? "";
ClientSecret = ConfigurationManager.AppSettings["ClientSecret"] ?? "";
RedirectUri = "https://developer.authorize.net/api/reference/index.html";
Read = true;
Write = true;
Expand Down Expand Up @@ -108,4 +117,4 @@ override public string ToString()
"\n";
}
}
}
}
16 changes: 8 additions & 8 deletions OAuthDemo/Views/Demo/Index.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
</div>
@Html.LabelFor(m => m.ClientSecret, new {@class = "col-md-2 control-label"})
<div class="col-md-10">
@Html.TextBoxFor(m => m.ClientSecret, new {@class = "form-control", Value = Model.ClientSecret})
@Html.PasswordFor(m => m.ClientSecret, new {@class = "form-control"})
</div>
</div>
<div class="form-group">
Expand Down Expand Up @@ -122,7 +122,7 @@
</div>
@Html.LabelFor(m => m.ClientSecret, new {@class = "col-md-2 control-label"})
<div class="col-md-10">
@Html.TextBoxFor(m => m.ClientSecret, new {@class = "form-control"})
@Html.PasswordFor(m => m.ClientSecret, new {@class = "form-control"})
</div>
</div>
<div class="form-group">
Expand All @@ -134,7 +134,7 @@
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<p>Response:</p>
@Html.TextArea("Step3Response", null, new {rows = 10, cols = 500, value = Model.Step3Response})
@Html.TextArea("Step3Response", null, new {rows = 10, cols = 500, value = Model.Step3Response, @readonly = "readonly"})
</div>
</div>
<a class="btn btn-default cont" href="#">Continue</a>
Expand All @@ -160,11 +160,11 @@
<div class="form-group">
@Html.LabelFor(m => m.AccessToken, new {@class = "col-md-2 control-label"})
<div class="col-md-10">
@Html.TextBoxFor(m => m.AccessToken, new {@class = "form-control"})
@Html.PasswordFor(m => m.AccessToken, new {@class = "form-control"})
</div>
@Html.LabelFor(m => m.CardNumber, new {@class = "col-md-2 control-label"})
<div class="col-md-10">
@Html.TextBoxFor(m => m.CardNumber, new {@class = "form-control"})
@Html.PasswordFor(m => m.CardNumber, new {@class = "form-control"})
</div>
@Html.LabelFor(m => m.ExpirationDate, new {@class = "col-md-2 control-label"})
<div class="col-md-10">
Expand Down Expand Up @@ -195,7 +195,7 @@
<div class="form-group">
@Html.LabelFor(m => m.AccessToken, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.TextBoxFor(m => m.AccessToken, new {@class = "form-control"})
@Html.PasswordFor(m => m.AccessToken, new {@class = "form-control"})
</div>
@Html.LabelFor(m => m.TransactionId, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
Expand Down Expand Up @@ -235,15 +235,15 @@
</div>
@Html.LabelFor(m => m.ClientSecret, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.TextBoxFor(m => m.ClientSecret, new { @class = "form-control"})
@Html.PasswordFor(m => m.ClientSecret, new { @class = "form-control"})
</div>
@Html.LabelFor(m => m.GrantType, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.TextBoxFor(m => m.GrantType, new { @class = "form-control", Value = "refresh_token" })
</div>
@Html.LabelFor(m => m.RefreshToken, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.TextBoxFor(m => m.RefreshToken, new { @class = "form-control" })
@Html.PasswordFor(m => m.RefreshToken, new { @class = "form-control" })
</div>
</div>

Expand Down
Loading