I'm trying to obtain a authorization token for OnTime OnDemand API using asp.net mvc. below are my simple action methods to obtain the token.
public ActionResult Index()
{
string eURL = HttpUtility.UrlEncode("http://localhost:2574/OnTimeClient/AuthorizationCodeCallback");
return Redirect("https://iss.ontimenow.com/auth?response_type=code&client_id=6457eb4e-42c7-4c19-ad25-73fc0d016e5a&redirect_uri=" +eURL+"&scope=read%20write");
}
public ActionResult AuthorizationCodeCallback(string code, string error)
{
return Redirect("https://iss.ontimenow.com/api/oauth2/token?grant_type=authorization_code&code="+code+"&redirect_uri=www.yahoo.com&client_id=6457eb4e-42c7-4c19-ad25-73fc0d016e5a&client_secret=8538c23b-211f-4351-bfe9-f533e81c97bf");
}
I'm receiving {"error":"invalid_grant"}.
I solved the issue. redirect_uri should be the same in both methods.
public ActionResult Index()
{
if(Request.QueryString["code"]==null)
{
string eURL = HttpUtility.UrlEncode("http://localhost:2574/OnTimeClient/AuthorizationCodeCallback");
return Redirect("https://iss.ontimenow.com/auth?response_type=code&client_id=6457eb4e-42c7-4c19-ad25-73fc0d016e&redirect_uri=" +eURL+"&scope=read%20write");
}
else
{
//process after obtaining the token
}
}
public ActionResult AuthorizationCodeCallback(string code, string error)
{
string eURL = HttpUtility.UrlEncode("http://localhost:2574/OnTimeClient/AuthorizationCodeCallback");
return Redirect("https://iss.ontimenow.com/api/oauth2/token?grant_type=authorization_code&code="+code+"&redirect_uri="+eURL+"&client_id=6457eb4e-42c7-4c19-ad25-73fc0d016e&client_secret=8538c23b-211f-4351-bfe9-f533e81c97bf");
}
Related
I need to reject the Request if the body has incomplete JSON.
I have a .NetCore API, which has a lot of properties. That API does a lot of operations and will be getting a lot of requests, so want to reject if the JSON is incomplete beforehand.
If, I have below AssignmentDetail class,
public class AssignmentDetail
{
public string Name { get; set; }
public string Address { get; set; }
}
Complete JSON Example
{
"Name":"dfsdfsdf",
"Address":"essdfsdfsd",
}
Incomplete JSON Example
{
"Name":"dfsdfsdf"
}
I have some approaches but need something which can be done through in startup, but just for that action.
Using custom Serializable for that AssignmentDetail model (Don't want this approach)
Creating a function to validate the Incomplete JSON like validateIncompleteJSON() (Don't want this approach)
Something in ConfigureServices(IServiceCollection services) but just for that controller action
[HttpPost]
[Route("ProcessAssignment")]
public async Task<AssignmentResponseModel> ProcessAssignment(AssignmentDetail model)
{
var response = new AssignmentResponseModel();
try
{
//can call function here to check the incomlpete JSON
//validateIncompleteJSON();
//var result = await _mediator.Send(queryDetails);
//response = result.Response;
}
catch (Exception ex)
{
throw exception;
}
return response;
}
I don't want to use the Serializable way, as the class is too big and will have to handle all the properties.
Data Annotations add Required Attribute to property
Codes of Model
public class AssignmentDetail
{
[Required]
public string Name { get; set; }
[Required]
public string Address { get; set; }
}
Codes of Controller
[HttpPost]
[Route("/ProcessAssignment")]
public IActionResult ProcessAssignment(AssignmentDetail model)
{
//var response = new AssignmentResponseModel();
//try
//{
// //can call function here to check the incomlpete JSON
// //validateIncompleteJSON();
// //var result = await _mediator.Send(queryDetails);
// //response = result.Response;
//}
//catch (Exception ex)
//{
// throw exception;
//}
//return response;
if (ModelState.IsValid)
{
return Ok("Success");
}
return BadRequest();
}
I have this simple piece of code:
public class TestController : System.Web.Mvc.Controller
{
/// <summary>
/// Used for test
/// </summary>
[ResponseType(typeof(string))]
public ActionResult Index()
{
try
{
// How to get multipart data here?
var result = new JsonResult();
result.Data = ValidateSignedRequest(content);
return result;
}
catch (Exception ex)
{
throw ex;
}
}
/// Example signed_request variable from request multipart/form-data
//private string VALID_SIGNED_REQUEST = "ZcZocIFknCpcTLhwsRwwH5nL6oq7OmKWJx41xRTi59E.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImV4cGlyZXMiOiIxMjczMzU5NjAwIiwib2F1dGhfdG9rZW4iOiIyNTQ3NTIwNzMxNTJ8Mi5JX2VURmtjVEtTelg1bm8zakk0cjFRX18uMzYwMC4xMjczMzU5NjAwLTE2Nzc4NDYzODV8dUk3R3dybUJVZWQ4c2VaWjA1SmJkekdGVXBrLiIsInNlc3Npb25fa2V5IjoiMi5JX2VURmtjVEtTelg1bm8zakk0cjFRX18uMzYwMC4xMjczMzU5NjAwLTE2Nzc4NDYzODUiLCJ1c2VyX2lkIjoiMTY3Nzg0NjM4NSJ9";
public bool ValidateSignedRequest(string request)
{
// ...
}
}
I found that with System.Web.Http.ApiController it's an easy thing:
[Route("test")]
[ResponseType(typeof(string))]
[HttpPost]
[AllowAnonymous]
public async Task<IHttpActionResult> Test()
{
var signedRequest = HttpContext.Current.Request.Params["my_data_key"];
return Ok(await Task.FromResult(signedRequest));
}
But here I have to use System.Web.Mvc.Controller and I couldn't find a solution yet.
Any idea? thanks!
I finally found out the workaround I was looking for. I don't know if it's the best approach, but it does the job!
You can access the form-data from the static instance of Request like the following:
[HttpPost]
[ResponseType(typeof(string))]
public ActionResult Index()
{
try
{
if (!Request.Form.AllKeys.Any() || Request.Form["signed_request"] == null)
throw new ArgumentNullException();
var result = new JsonResult();
string signedRequest = Request.Form["signed_request"];
result.Data = ValidateSignedRequest(signedRequest);
return result;
}
catch (Exception ex)
{
throw ex;
}
}
I'd like to wrap IHttpActionResult because I need some extra data to be consumed by the client app.
My first approach was to create and return simple DTO, wrapping result object if succeeded:
Response DTO:
public class Response<T>
{
public string ErrorMessage { get; set; }
public bool Success { get; set; }
public string CodeStatus { get; set; }
public T Result { get; set; }
public Response(bool isSuccess, [Optional] T result, [Optional] string codeStatus, [Optional] string errorMessage)
{
Success = isSuccess;
Result = result;
CodeStatus = codeStatus;
ErrorMessage = errorMessage;
}
}
Controller:
public IHttpActionResult Get(int id)
{
return BadRequest(new Response<MyObjectClass>(false, null,"Invalid Id",400));
...
return Ok(new Response<MyObjectClass>(true, result);
}
I've found it very ineffective way to deal with wrapping. I dont find it very elegant way. I've tried to figured out some generic solution and ended up with the following:
Example Controller Action:
public IHttpActionResult GetById(int id)
{
var result = _someService.Get(id);
if (result == null)
return NotFound().WithError("Invalid Id");
return Ok().WithSuccess(result);
}
This still returns Response DTO.
I've wrapped IHttpActionResult to deal with creating Response DTO:
public class HttpActionResult : IHttpActionResult
{
private readonly string _errorMessage;
private readonly IHttpActionResult _innerResult;
private readonly object _result;
private readonly bool _isSuccess;
public HttpActionResult(IHttpActionResult inner, bool isSuccess, object result,string errorMessage)
{
_errorMessage = errorMessage;
_innerResult = inner;
_result = result;
_isSuccess = isSuccess;
}
public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
HttpResponseMessage response = await _innerResult.ExecuteAsync(cancellationToken);
response.Content = new ObjectContent(typeof(Response), new Response(_isSuccess, _result, ((int)response.StatusCode).ToString(), _errorMessage), new JsonMediaTypeFormatter());
return response;
}
}
Finally I've added extension methods to IHttpActionResult to easier use in controller:
public static class IHttpActionResultExtensions
{
public static IHttpActionResult WithSuccess(this IHttpActionResult inner, object result = null, string message = null)
{
return new HttpActionResult(inner, true, result, message);
}
public static IHttpActionResult WithError(this IHttpActionResult inner, string message = null)
{
return new HttpActionResult(inner, false,null, message);
}
}
What are the alternatives to deal with wrapping http messages in API Controller?
What weak points do you see in my solution?
BTW, I see some weak points on your approach:
WebAPI is meant to be used to create RESTful Web services. Why are you trying to provide another layer of status and other details? HTTP is rich enough to cover these requirements. For example, you can use standard status codes and a subcode as follows: 500.1, 500.2.
Success or failure is easier to express with HTTP status codes. 2XX range for successful operations, and for an unsuccessful one you can use, for example, 400 (Bad Request). 401 for an unauthorized access... 500 for a server failure...
WebAPI already provides ModelState to let the framework build a response object. Use it and try to don't re-invent the wheel.
Again, keep it simple. Response entity goes on the response body. Success or failure is expressed by status codes. Details about a bad request are added to the ModelState dictionary. An error message should be set to the response's ReasonPhrase.
IHttpActionResult implementations are meant to transform your domain result into an HTTP response. That is, you're in the right track excepting when you try to return your response object as is. My advise is you should use your IHttpActionResult to set every detail on your own response object to standard HTTP semantics, and notify errors using ModelState out-of-the-box approach which works well.
Avoid IHttpActionResult and use HttpResponseException with Business Entity as result type. As in your solution, you cannot write statically typed test cases.
For example,
protected void ThrowHttpError(HttpStatusCode statusCode, string message)
{
throw new HttpResponseException(
new HttpResponseMessage(statusCode) {
ReasonPhrase = message,
// HTTP 2.0 ignores ReasonPhrase
// so we send ReasonPhrase again in the Content
Content = new StringContent(message)
});
}
// some generic option...
protected void ThrowHttpError<T>(HttpStatusCode statusCode, T content)
where T:class
{
throw new HttpResponseException(
new HttpResponseMessage(statusCode) {
ReasonPhrase = "Error",
Content = JsonConvert.Serialize(content)
});
}
Your methods,
public async Task<Product> Get(long id){
var product = await context.Products
.FirstOrDefaultAsync( x=> x.ProductID == id);
if(product==null){
ThrowHttpError(HttpStatusCode.NotFound,
$"Product not found for {id}");
}
if(product.RequiresValidation){
// generic version....
ThrowHttpError(HttpStatusCode.Conflict,
new Product{
ProductID = product.ProductID,
ValidationRequestCode = product.ValidationRequestCode
});
}
return product;
}
For further more, you can customise method ThrowHttpError to suite your needs. Best part is, it is still testable.
I am trying to create an authentication provider in DotNetNuke 7.4 which supports LinkedId. I have used the source package for the Facebook provider from the DnnPlatform GIT as the base and have modified it for LinkedIn's oAuth. I am able to connect via LinkedIn and get the auth token but the code fails on
OAuthClient.GetCurrentUser<LinkedInUserData>();
due to LinkedInUserData being null. the specific logged error is
DotNetNuke.Services.Exceptions.Exceptions - ~/Default.aspx?tabid=55&error=An unexpected error has occurred
System.ArgumentNullException: Value cannot be null.
Parameter name: value
at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings)
at DotNetNuke.Services.Authentication.OAuth.OAuthClientBase.GetCurrentUser[TUserData]()
at DotNetNuke.Authentication.LinkedIn.Login.GetCurrentUser() in c:\Websites\dnndev74_2\DesktopModules\AuthenticationServices\LinkedIn\Login.ascx.cs:line 103
Below are the 3 classes that are in play, there is a lot of inheritance going on so I'm having trouble understanding the mechanism for how LinkedInUserData gets populated in the first place. On a note. when I take the facebook codebase from GIT and install it as a provider on my local, and try to register with facebook account, I get the same error. However, if I install the provider through the CMS it runs fine or use the dll that comes with the dnn 7.4 install, facebook works. So I am lead to believe there is something fundamentally flawed with the GIT code..
LinkedInClient.cs
namespace DotNetNuke.Authentication.LinkedIn.Components
{
public class LinkedInClient : OAuthClientBase
{
#region Constructors
public LinkedInClient(int portalId, AuthMode mode)
: base(portalId, mode, "LinkedIn")
{
base.AuthorizationEndpoint = new Uri("https://www.linkedin.com/uas/oauth2/authorization");
base.RequestTokenEndpoint = new Uri("https://api.linkedin.com/uas/oauth/requestToken?scope=r_emailaddress");
base.TokenMethod = HttpMethod.POST;
base.TokenEndpoint = new Uri("https://www.linkedin.com/uas/oauth2/accessToken");
base.MeGraphEndpoint = new Uri("https://api.linkedin.com/v1/people/~:(id,first-name,last-name,email-address,formatted-name,picture-url)?format=json");
base.AuthTokenName = "LinkedInUserToken";
base.OAuthVersion = "2.0";
base.LoadTokenCookie(string.Empty);
}
#endregion
protected override TimeSpan GetExpiry(string responseText)
{
var jsonSerializer = new JavaScriptSerializer();
var tokenDictionary = jsonSerializer.DeserializeObject(responseText) as Dictionary<string, object>;
return new TimeSpan(0, 0, Convert.ToInt32(tokenDictionary["expires_in"]));
}
protected override string GetToken(string responseText)
{
var jsonSerializer = new JavaScriptSerializer();
var tokenDictionary = jsonSerializer.DeserializeObject(responseText) as Dictionary<string, object>;
return Convert.ToString(tokenDictionary["access_token"]);
}
}
}
LinkedInUserData.cs
namespace DotNetNuke.Authentication.LinkedIn.Components
{
[DataContract]
[Serializable]
public class LinkedInUserData : UserData
{
#region Overrides
public override string FirstName
{
get { return LinkedInFirstName; }
set { }
}
public override string LastName
{
get { return LinkedInLastName; }
set { }
}
public override string Email
{
get { return emailAddress; }
set { }
}
public override string ProfileImage
{
get { return LinkedInPictureUrl; }
set { }
}
#endregion
[DataMember(Name = "first-name")]
public string LinkedInFirstName { get; set; }
[DataMember(Name = "last-name")]
public string LinkedInLastName { get; set; }
[DataMember(Name = "picture-url")]
public string LinkedInPictureUrl { get; set; }
[DataMember(Name = "email-address")]
public string emailAddress { set; get; }
}
Login.cs
namespace DotNetNuke.Authentication.LinkedIn
{
public partial class Login : OAuthLoginBase
{
protected override string AuthSystemApplicationName
{
get { return "LinkedIn"; }
}
public override bool SupportsRegistration
{
get { return true; }
}
protected override UserData GetCurrentUser()
{
return OAuthClient.GetCurrentUser<LinkedInUserData>();
}
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
loginButton.Click += loginButton_Click;
registerButton.Click += loginButton_Click;
OAuthClient = new LinkedInClient(PortalId, Mode);
loginItem.Visible = (Mode == AuthMode.Login);
registerItem.Visible = (Mode == AuthMode.Register);
}
protected override void AddCustomProperties(NameValueCollection properties)
{
base.AddCustomProperties(properties);
properties.Add("LinkedIn", OAuthClient.GetCurrentUser<LinkedInUserData>().Link.ToString());
}
private void loginButton_Click(object sender, EventArgs e)
{
AuthorisationResult result = OAuthClient.Authorize();
if (result == AuthorisationResult.Denied)
{
UI.Skins.Skin.AddModuleMessage(this, Localization.GetString("PrivateConfirmationMessage", Localization.SharedResourceFile), ModuleMessage.ModuleMessageType.YellowWarning);
}
}
}
}
Mark,
I wrote a DNN provider for Linkedin a couple years ago. Comparing my code to yours, the first thing mine does is redirect the user in order to get an access token. The base url for obtaining the access token and permission from the user is: www.linkedin.com/uas/oauth2. I must pass my linked API key, a redirect Url and a few other pieces of data which comes from the provider settings.
My redirect Url is the same as the default portal login page which loads my provider. Once the user allows LinkedIn to allow my application access and verifies he is logged in, the redirect back to my provider will look for the LinkedInAuthToken cookie in the Request.Cookies. Once I verify the token is valid, I can make an additional API call to the /v1/people/ API to get user data to complete any kind of auto-registration or auto profile updates to DNN.
It seems like your provider is immediately attempting the user data lookup API call before obtaining the oauth access cookie.
The code for my LinkedIn provider is not open source, but I suppose I could get permission from my work to make it public. Message me if you are interested in it.
How do I convert the following Ninject DI to the equivalent for LightInject DI? I'm having issues with getting to the right syntax.
Database.SetInitializer(new MigrateDatabaseToLatestVersion<DefaultMembershipRebootDatabase, BrockAllen.MembershipReboot.Ef.Migrations.Configuration>());
kernel.Bind<UserAccountService>().ToSelf();
kernel.Bind<AuthenticationService>().To<SamAuthenticationService>();
kernel.Bind<IUserAccountQuery>().To<DefaultUserAccountRepository>().InRequestScope();
kernel.Bind<IUserAccountRepository>().To<DefaultUserAccountRepository>().InRequestScope();
On my original question, I didn't include this, but this (also posted as comment to this post) was the not working code I attempted to make it work:
Database.SetInitializer(new MigrateDatabaseToLatestVersion<DefaultMembershipRebootDatabase, BrockAllen.MembershipReboot.Ef.Migrations.Configuration>());
container.Register<UserAccountService>();
container.Register<AuthenticationService, SamAuthenticationService>();
container.Register<IUserAccountQuery, DefaultUserAccountRepository>(new PerRequestLifeTime());
container.Register<IUserAccountRepository, DefaultUserAccountRepository>(new PerRequestLifeTime());
The error message (without the stack trace) given was this:
Exception Details: System.InvalidOperationException: Unresolved dependency [Target Type: BrockAllen.MembershipReboot.Ef.DefaultUserAccountRepository], [Parameter: ctx(BrockAllen.MembershipReboot.Ef.DefaultMembershipRebootDatabase)], [Requested dependency: ServiceType:BrockAllen.MembershipReboot.Ef.DefaultMembershipRebootDatabase, ServiceName:]
Source Error:
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.
*If anyone wants to see the stack trace too - * just ask, and I'll post it in a reply to this question.
The constructor for DefaultMembershipRebootDatabase (as was in a sample project, my project used the dll provided through nuget, and the constructor wasn't available, but I'm pretty sure they're more than likely the same in both cases (seeing as how it comes from the same source...) is:
public class DefaultMembershipRebootDatabase : MembershipRebootDbContext<RelationalUserAccount>
{
public DefaultMembershipRebootDatabase()
: base()
{
}
public DefaultMembershipRebootDatabase(string nameOrConnectionString)
: base(nameOrConnectionString)
{
}
public DefaultMembershipRebootDatabase(string nameOrConnectionString, string schemaName)
: base(nameOrConnectionString, schemaName)
{
}
}
This is the constructor (as was in the same aforementioned sample project) for the DefaultUserAccountRepository:
public class DefaultUserAccountRepository
: DbContextUserAccountRepository<DefaultMembershipRebootDatabase, RelationalUserAccount>,
IUserAccountRepository
{
public DefaultUserAccountRepository(DefaultMembershipRebootDatabase ctx)
: base(ctx)
{
}
IUserAccountRepository<RelationalUserAccount> This { get { return (IUserAccountRepository<RelationalUserAccount>)this; } }
public new UserAccount Create()
{
return This.Create();
}
public void Add(UserAccount item)
{
This.Add((RelationalUserAccount)item);
}
public void Remove(UserAccount item)
{
This.Remove((RelationalUserAccount)item);
}
public void Update(UserAccount item)
{
This.Update((RelationalUserAccount)item);
}
public new UserAccount GetByID(System.Guid id)
{
return This.GetByID(id);
}
public new UserAccount GetByUsername(string username)
{
return This.GetByUsername(username);
}
UserAccount IUserAccountRepository<UserAccount>.GetByUsername(string tenant, string username)
{
return This.GetByUsername(tenant, username);
}
public new UserAccount GetByEmail(string tenant, string email)
{
return This.GetByEmail(tenant, email);
}
public new UserAccount GetByMobilePhone(string tenant, string phone)
{
return This.GetByMobilePhone(tenant, phone);
}
public new UserAccount GetByVerificationKey(string key)
{
return This.GetByVerificationKey(key);
}
public new UserAccount GetByLinkedAccount(string tenant, string provider, string id)
{
return This.GetByLinkedAccount(tenant, provider, id);
}
public new UserAccount GetByCertificate(string tenant, string thumbprint)
{
return This.GetByCertificate(tenant, thumbprint);
}
}
And this is the controller in my project:
namespace brockallen_MembershipReboot.Controllers
{
using System.ComponentModel.DataAnnotations;
using BrockAllen.MembershipReboot;
using BrockAllen.MembershipReboot.Mvc.Areas.UserAccount.Models;
public class UserAccountController : Controller
{
UserAccountService _userAccountService;
AuthenticationService _authService;
public UserAccountController(AuthenticationService authService)
{
_userAccountService = authService.UserAccountService;
_authService = authService;
}
// GET: /UserAccount/
[Authorize]
public ActionResult Index()
{
return View();
}
public ActionResult Login()
{
return View(new LoginInputModel());
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginInputModel model)
{
if (ModelState.IsValid)
{
/*BrockAllen.MembershipReboot.*/UserAccount account;
if (_userAccountService.AuthenticateWithUsernameOrEmail(model.Username, model.Password, out account))
{
_authService.SignIn(account, model.RememberMe);
_authService.SignIn(account, model.RememberMe);
/*if (account.RequiresTwoFactorAuthCodeToSignIn())
{
return RedirectToAction("TwoFactorAuthCodeLogin");
}
if (account.RequiresTwoFactorCertificateToSignIn())
{
return RedirectToAction("CertificateLogin");
}
if (_userAccountService.IsPasswordExpired(account))
{
return RedirectToAction("Index", "ChangePassword");
}*/
if (Url.IsLocalUrl(model.ReturnUrl))
{
return Redirect(model.ReturnUrl);
}
return RedirectToAction("Index");
}
else
{
ModelState.AddModelError("", "Invalid Username or Password");
}
}
return View(model);
}
public ActionResult Register()
{
return View(new RegisterInputModel());
}
[ValidateAntiForgeryToken]
[HttpPost]
public ActionResult Register(RegisterInputModel model)
{
if (ModelState.IsValid)
{
try
{
var account = _userAccountService.CreateAccount(model.Username, model.Password, model.Email);
ViewData["RequireAccountVerification"] = _userAccountService.Configuration.RequireAccountVerification;
return View("Success", model);
}
catch (ValidationException ex)
{
ModelState.AddModelError("", ex.Message);
}
}
return View(model);
}
}
}
The constructor for AuthenicationService is:
public abstract class AuthenticationService : AuthenticationService<UserAccount>
{
public new UserAccountService UserAccountService
{
get { return (UserAccountService)base.UserAccountService; }
set { base.UserAccountService = value; }
}
public AuthenticationService(UserAccountService userService)
: this(userService, null)
{
}
public AuthenticationService(UserAccountService userService, ClaimsAuthenticationManager claimsAuthenticationManager)
: base(userService, claimsAuthenticationManager)
{
}
}
By default, LightInject does not resolve concrete classes without registering them, while NInject does.
For example, NInject can resolve DefaultMembershipRebootDatabase without registering it, while LightInject cannot by default. Take a look at this.
In any way, to fix your issue, make sure that you register your concrete classes (that are needed as dependencies in other classes). Here is an example:
container.Register<DefaultMembershipRebootDatabase>();
I am assuming here that some class has a dependency on the concrete class DefaultMembershipRebootDatabase. If you have other concrete class dependencies, make sure that you also register them.
You should use the PerScopeLifetime rather than the PerRequestLifetime. PerRequestLifetime represents a transient lifetime that tracks disposable instances and disposes them when the scope ends. PerScopeLifetime ensures the same instance within a scope which in this case means the same instance within a web request.