I'm trying using OAuth2 on my MVC 5 web app but I'm getting trouble with OAuth2 from Microsoft. When I signed on using Microsoft it returned error=access_denied. Below are my configurations:
Set up public key, secret key and password http://apps.dev.microsoft.com/ . (You must create key pair and password)
Set up return Url https://localhost:44300/sign-microsoft
Add these key pair which are application Id and secret key to Startup.Auth.cs
app.UseMicrosoftAccountAuthentication(
new MicrosoftAccountAuthenticationOptions()
{
ClientId = "xxxx",
ClientSecret = "xxx",
Scope = { "wl.basic", "wl.emails" }
});
Below is action
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult ExternalLogin(string provider, string returnUrl)
{
if (provider.Equals("Microsoft"))
returnUrl = "https://localhost:44300/signin-microsoft";
// Request a redirect to the external login provider
return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl =
returnUrl }));
}
I've looked around someone got trouble and solved by using https. I have spent several days but I can't find out the problem.
Related
Im new to C# and im struggling with authorization in ASP.Net Core 3.1 MVC web application.I know that there is a lot of instruction on google, i've been reading and watching for 3 days but can not work this out because every instruction i found, it use another way and im really confused.
The idea of my system is:
Step 1. I POST username and password to my API and it'll response with JWT Token (if account is correct)
Step 2. I decode the token and get the username, email, role for my website, set HttpClient header for another requests.
My problems:
How and where to set HttpClient header (with my token) only one time when user login
How to force users stay at the Login page if they aren't login yet
Here's my Login method
[HttpPost, AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginViewModel account)
{
string url = "accounts/signin";
var response = await new HttpClientHelper<LoginViewModel>().PostRequest(url, account);
var userToken = JsonConvert.DeserializeObject<UserToken>(response);
Console.Out.WriteLine(userToken.Token);
if (userToken.Token != null)
{
var token = new JwtSecurityToken(jwtEncodedString: userToken.Token);
var userId = token.Claims.First(c => c.Type == "userId").Value;
var username = token.Claims.First(c => c.Type == "unique_name").Value;
var role = token.Claims.First(c => c.Type == "role").Value;
HttpContext.Session.SetString("token", token.ToString());
HttpContext.Session.SetString("userId", userId);
HttpContext.Session.SetString("username", username);
HttpContext.Session.SetString("role", role);
return RedirectToAction("Home", "Index");
}
return RedirectToAction("Login", "Login");
}
My model to receive response from api
public class UserToken
{
public string Token { get; set; }
public string ValidFrom { get; set; }
public string ValidTo { get; set; }
}
FYI: Ive already recived the response from api and got the Token, but ive to set HttpClient header every time i make a request..
How and where to set HttpClient header (with my token) only one time when user login
As far as I know, we couldn't set the httpclient header only one time when user login. Normally, we could store the token into session or cookie and then read it from cookie or session when you want to send request to web api.
How to force users stay at the Login page if they aren't login yet
For this requirement, I suggest you could consider using the authentication middleware to achieve your requirement.
You could check the user's session inside this middleware, if this user doesn't contains the session then you could modify the request path to login page.
More details, you could refer to below example:
//Below cods should add after app.usesession in startup.cs Configure method
app.Use((context, next) =>
{
string token = context.Session.GetString("token");
if (token == null)
{
context.Request.Path = "/account/login";
}
return next.Invoke();
});
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I have created one Asp core web api which will be consumed by C# console application outside the organization. This console application is scheduled to run periodically . So hits on Web api will come when console application run.
Please assist How Can i secure my Web Api to malware hit or unauthentic access. I can't use AD authentication as I am unable to register client application in AAD(Azure active directory) Please assist.
Generally speaking , there're lots ways to do that . For example , use a basic scheme authentication in which the client sends username:password with the base64-encoding . However . It's not that safe .
I suggest you use JWT token . The authentication of Jwt scheme is dead simple :
The client send a request to ask for a JWT token with client_id and client_key . (You might configure them in configuration file or database on server)
Tf the client_id and client_key matches , the Server send a response with a JWT access token , maybe an additional refresh token if you like ; otherwise , send a response with a 401.
The client consumes webapi with a Authorization: Bearer ${access_token} header. The server will decrypt the access_token and hit the correct action if valid.
Here's a how-to in details:
Dummy class to hold information
To represent the client_id and client_key sent by your console , Let's create a dummy Dto class :
public class AskForTokenRequest
{
public string ClientId { get; set; }
public string ClientKey { get; set; }
}
When creating and validating Jwt token , we need information about issuer , audience , and secret keys . To hold these information , let's create another dummy class :
public class SecurityInfo {
public static readonly string Issuer = "xxx";
public static readonly string[] Audiences = new[] { "yyy" };
public static readonly string SecretKey = "!##$%^&*()&!!!##$%^&*()&!!!##$%^&*()&!!!##$%^&*()&!!!##$%^&*()&!";
}
Before we move on , let's create a JwtTokenHelper to generate token :
The JwtTokenHelper helps to validate client_id & client_key and generate Jwt Token .
public class JwtTokenHelper
{
//private AppDbContext _dbContext { get; set; }
//public JwtTokenHelper(AppDbContext dbContext) {
// this._dbContext = dbContext;
//}
public virtual bool ValidateClient(string clientId, string clientKey)
{
// check the client_id & clientKey with database , config file , or sth else
if (clientId == "your_console_client_id" && clientKey == "your_console_client_key")
return true;
return false;
}
/// construct a token
public virtual JwtSecurityToken GenerateToken(string clientId, DateTime expiry, string audience)
{
ClaimsIdentity identity = new ClaimsIdentity(new GenericIdentity(clientId, "jwt"));
var token=new JwtSecurityToken
(
claims: identity.Claims,
issuer: SecurityInfo.Issuer,
audience: audience,
expires: expiry,
notBefore: DateTime.UtcNow,
signingCredentials: new SigningCredentials(
new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecurityInfo.SecretKey)),
SecurityAlgorithms.HmacSha256
)
);
return token;
}
public virtual string GenerateTokenString(string clientId, DateTime expiry,string audience)
{
// construct a jwt token
var token = GenerateToken(clientId,expiry,audience);
// convert the token to string
JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
return tokenHandler.WriteToken(token);
}
}
Configure the server to enable JwtBearer authentication :
Add JwtTokenHelper to DI Container and Add authentication scheme of JwtBearer
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<JwtTokenHelper>();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.RequireHttpsMetadata = false;
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = SecurityInfo.Issuer,
ValidAudiences = SecurityInfo.Audiences,
ValidateAudience = true,
ValidateIssuer = true,
ValidateIssuerSigningKey = true,
IssuerSigningKeys = new List<SecurityKey> {
new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecurityInfo.SecretKey) )
},
ValidateLifetime = true,
ClockSkew = TimeSpan.FromMinutes(60)
};
});
services.AddMvc();
}
Don't forget to add app.UseAuthentication(); in your Configure() method .
How to use:
Now , Create a controller to generate Jwt token
[Route("/api/token")]
public class TokenController : Controller
{
private readonly JwtTokenHelper _tokenHelper;
public TokenController(JwtTokenHelper tokenHelper) {
this._tokenHelper = tokenHelper;
}
[HttpPost]
public IActionResult Create([FromBody] AskForTokenRequest client)
{
if(! this._tokenHelper.ValidateClient(client.ClientId , client.ClientKey))
return new StatusCodeResult(401);
DateTime expiry = DateTime.UtcNow.AddMinutes(60); // expires in 1 hour
var audience = "yyy";
var access_token = this._tokenHelper.GenerateTokenString(client.ClientKey, expiry,audience);
return new JsonResult(new {
access_token = access_token,
});
}
}
and protect you webapi with [Authorize] attribute :
public class HomeController : Controller
{
[Authorize]
public IActionResult GetYourWebApiMethod(){
return new ObjectResult(new {
Username = User.Identity.Name
});
}
}
I'm making a web application by using Web API 2 and MVC 5.
My app has api :
api/account/login, which is used for checking posted information and throw status 200 when an account is granted to access application.
Also, I have one view : /Home/Index which is only available to authenticated client.
Now, my approach is :
Call api/account/login, receive the cookie thrown from that api.
Attach thrown back cookie to browser.
When user access /Home/Index, view is available for him/her.
My questions are :
- Is my approach possible ?
- How can I encrypt my cookie in Web API 2 like MVC 5 does to its cookie ?
Thank you,
The best way to achieve this to have a authorization server (a webAPI generating a token) and token consumption middle ware in your MVC project.IdentityServer https://github.com/IdentityServer/IdentityServer3 should help. However I have done this as below
Built an authorization server using JWT with WEB API and ASP.Net Identity as explained here http://bitoftech.net/2015/02/16/implement-oauth-json-web-tokens-authentication-in-asp-net-web-api-and-identity-2/
once you do that your webAPIs startup.cs will look like below
/// Configures cookie auth for web apps and JWT for SPA,Mobile apps
private void ConfigureOAuthTokenGeneration(IAppBuilder app)
{
// Configure the db context, user manager and role manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
//Cookie for old school MVC application
var cookieOptions = new CookieAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
CookieHttpOnly = true, // JavaScript should use the Bearer
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/api/Account/Login"),
CookieName = "AuthCookie"
};
// Plugin the OAuth bearer JSON Web Token tokens generation and Consumption will be here
app.UseCookieAuthentication(new CookieAuthenticationOptions());
OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
//For Dev enviroment only (on production should be AllowInsecureHttp = false)
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/oauth/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(30),
Provider = new CustomOAuthProvider(),
AccessTokenFormat = new CustomJwtFormat(ConfigurationManager.AppSettings["JWTPath"])
};
// OAuth 2.0 Bearer Access Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
}
You can find CustomOAuthProvider,CustomJwtFormat classes here https://github.com/tjoudeh/AspNetIdentity.WebApi/tree/master/AspNetIdentity.WebApi/Providers
Write a consumption logic (i.e. middleware) in all my other APIs (Resource servers) that you want to secure using same token. Since you want to consume the token generated by webAPI in your MVC project, after implementing Authorization server you need to do below
In your MVC app add below in startup.cs
public void Configuration(IAppBuilder app)
{
ConfigureOAuthTokenConsumption(app);
}
private void ConfigureOAuthTokenConsumption(IAppBuilder app)
{
var issuer = ConfigurationManager.AppSettings["AuthIssuer"];
string audienceid = ConfigurationManager.AppSettings["AudienceId"];
byte[] audiencesecret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["AudienceSecret"]);
app.UseCookieAuthentication(new CookieAuthenticationOptions { CookieName = "AuthCookie" , AuthenticationType=DefaultAuthenticationTypes.ApplicationCookie });
//// Api controllers with an [Authorize] attribute will be validated with JWT
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Passive,
AuthenticationType = "JWT",
AllowedAudiences = new[] { audienceid },
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(issuer, audiencesecret)
}
});
}
In your MVC controller when you receive the token de-serialize it and generate a cookie from the access token
AccessClaims claimsToken = new AccessClaims();
claimsToken = JsonConvert.DeserializeObject<AccessClaims>(response.Content);
claimsToken.Cookie = response.Cookies[0].Value;
Request.Headers.Add("Authorization", "bearer " + claimsToken.access_token);
var ctx = Request.GetOwinContext();
var authenticateResult = await ctx.Authentication.AuthenticateAsync("JWT");
ctx.Authentication.SignOut("JWT");
var applicationCookieIdentity = new ClaimsIdentity(authenticateResult.Identity.Claims, DefaultAuthenticationTypes.ApplicationCookie);
ctx.Authentication.SignIn(applicationCookieIdentity);
Generate a machine key and add it in web.config of your webAPI and ASP.Net MVC site.
With this a cookie will be created and [Authorize] attribute in MVC Site and WebAPI will honor this cookie.
P.S. - I have done this with a web API issuing JWT (Authorization server or Auth & resource server) and successfully able to consume in a ASP.Net MVC website, SPA Site built in Angular , secure APIs built in python (resource server) , spring (resource server), Android App.
You could set the cookie once the user has authenticated against the Account controller.
public class AccountController
{
public HttpResponseMessage Login()
{
// Your authentication logic
var responseMessage = new HttpResponseMessage();
var cookie = new CookieHeaderValue("session-id", "12345");
cookie.Expires = DateTimeOffset.Now.AddDays(1);
cookie.Domain = Request.RequestUri.Host;
cookie.Path = "/";
responseMessage.Headers.AddCookies(new CookieHeaderValue[] { cookie });
return responseMessage;
}
}
To authenticate you can put the [Authenticate] attribute on your Home controller.
public class HomeController
{
[Authenticate]
public ActionResult Index()
{
return View();
}
}
The Authenticate attribute can also be applied at the Controller level if needed.
[Authenticate]
public class HomeController
{
}
You can also make your own authorization attribute if needed by overriding AuthorizeCore and checking for a valid cookie:
public class CustomAuth : AuthenticationAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
HttpCookie authCookie = httpContext.Request.Cookies["CookieName"];
// Your logic
return true;
}
}
I have done much searching and have not been able to find an ideal solution for this issue. I know that there is an alleged solution (WebApi ASP.NET Identity Facebook login) however, some elements of the solution are a (in my mind) seriously hacky (e.g. registering the user with a regular account and then adding the external login, rather than registering them with the external login).
I would like to be able to register and authenticate against an ASP.NET Web API 2 application, after already having used the Facebook SDK login on a iOS mobile app, i.e. I have already authenticated against Facebook using their SDK, and now want to seamlessly register/authenticate with the ASP.NET Web API. I do not want to use the process where I have to use the web calls (/api/Account/ExternalLogin) as this, well, is not a great user experience on a native mobile app.
I have tried learning about OWIN, but the .NET framework is complex and I am struggling in how to solve this issue.
I needed to do this today for my Ionic app. The Web API Account controller has its own opinion on how to do things and the best way to understand it is reading this pretty amazing 3 part blog post by Dominick Baier. https://leastprivilege.com/2013/11/26/dissecting-the-web-api-individual-accounts-templatepart-3-external-accounts/.
The way I worked around it was to forget the out-of-the-box flow, but instead use the accessToken from the native Facebook login and then call into the following server code to 1) call the Facebook API to validate the access token, 2) from that Facebook call, get the email and id, 3) either get the user or create it (and login) which is already code that's in the Account controller in other places, 4) Create the local authority JWT for subsequent Web API calls.
public class ProviderAndAccessToken {
public string Token { get; set; }
public string Provider { get; set; }
}
[AllowAnonymous]
[HttpPost]
[Route("JwtFromProviderAccessToken")]
public async Task<IHttpActionResult> JwtFromProviderAccessToken(ProviderAndAccessToken model) {
string id = null;
string userName = null;
if (model.Provider == "Facebook") {
var fbclient = new Facebook.FacebookClient(model.Token);
dynamic fb = fbclient.Get("/me?locale=en_US&fields=name,email");
id = fb.id;
userName = fb.email;
}
//TODO: Google, LinkedIn
ApplicationUser user = await UserManager.FindAsync(new UserLoginInfo(model.Provider, id));
bool hasRegistered = user != null;
string accessToken = null;
var identity = new ClaimsIdentity(OAuthDefaults.AuthenticationType);
var props = new AuthenticationProperties() {
IssuedUtc = DateTime.UtcNow,
ExpiresUtc = DateTime.UtcNow.Add(Startup.OAuthOptions.AccessTokenExpireTimeSpan),
};
if (hasRegistered) {
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(UserManager,
OAuthDefaults.AuthenticationType);
ClaimsIdentity cookieIdentity = await user.GenerateUserIdentityAsync(UserManager,
CookieAuthenticationDefaults.AuthenticationType);
AuthenticationProperties properties = ApplicationOAuthProvider.CreateProperties(user.UserName);
Authentication.SignIn(properties, oAuthIdentity, cookieIdentity);
identity.AddClaim(new Claim(ClaimTypes.Name, user.UserName));
identity.AddClaim(new Claim("role", "user"));
}
else {
user = new ApplicationUser() { UserName = userName, Email = userName };
IdentityResult result = await UserManager.CreateAsync(user);
if (!result.Succeeded) {
return GetErrorResult(result);
}
result = await UserManager.AddLoginAsync(user.Id, new UserLoginInfo(model.Provider, id));
if (!result.Succeeded) {
return GetErrorResult(result);
}
identity.AddClaim(new Claim(ClaimTypes.Name, userName));
}
identity.AddClaim(new Claim("role", "user"));
var ticket = new AuthenticationTicket(identity, props);
accessToken = Startup.OAuthOptions.AccessTokenFormat.Protect(ticket);
return Ok(accessToken);
}
The code I'm using in Ionic basically does this to get the access token from Facebook, then call the Web API to get a local authority JWT to use as the Bearer token.
Facebook.login(['public_profile', 'email']).then((result) => {
return this.http.post("<URL>/api/Account/JwtFromProviderAccessToken", { provider: "Facebook", token: result.authResponse.accessToken })
.map((res: Response) => res.json())
.catch(this.handleError)
.subscribe((res: Response) => {
// use the result as the Bearer token
});
})...
Seems pretty safe, but understand that I'm not security expert so this code comes without warranty and please let me know if you see anything glaring and I'll update the code.
I have an ASP.NET MVC app in which I am trying to have use DotNetOpenAuth for my Google OAuth. I am using the GoogleConsumer class from the sample, and attempting to do the first step of the authentication. The code below is essentially the same as that in the provided WebForms application, just in an MVC controller:
public string Authenticate()
{
GoogleTokenManager tokenManager = new GoogleTokenManager(ConsumerKey, ConsumerSecret);
WebConsumer webConsumer = new WebConsumer(GoogleConsumer.ServiceDescription, tokenManager);
GoogleConsumer.RequestAuthorization(webConsumer, GoogleConsumer.Applications.Gmail);
return "";
}
The code executes when I make an AJAX request to the controller, but I am never redirected to the Google page for authentication.
The underlying request was returning a 302 redirect response which I wasn't handling properly. What I found to be more helpful was to specify the callback URL to another action in my controller, as follows:
public ActionResult Authenticate()
{
string callbackUrl = Request.Url.ToString().Replace("Authenticate", "OtherAction");
Uri callback = new Uri(callbackUrl);
WebConsumer webConsumer = new WebConsumer(GoogleConsumer.ServiceDescription, TokenManager);
Dictionary<string, string> extraParameters = new Dictionary<string, string>();
extraParameters.Add("scope", GoogleConsumer.GetScopeUri(GoogleConsumer.Applications.Gmail));
UserAuthorizationRequest request = webConsumer.PrepareRequestUserAuthorization(callback, extraParameters, null);
return webConsumer.Channel.PrepareResponse(request).AsActionResult();
}
public ActionResult OtherAction()
{
// oauth_verifier, oauth_token are now in the RequestQueryString
}