I'm implementing a custom CredentialsAuthProvider in ServiceStack and it works great. My only problem is that I would like to return more information back in my AuthResponse. I'm using the following override to authenticate in my custom provider:
public override bool TryAuthenticate(ServiceStack.ServiceInterface.IServiceBase authService, string userName, string password)
{
// Authenticates user and adds values to session
}
The response I get after successful authentication is the out of the box response that the Auth plugin provides. Is there a way to add more values or to customize the response upon successful login? I don't see any overrides that would allow it.
Related
Using ASP.NET Core 3.1 I am creating an User's Email confirmation token to send by email:
String token = await _userManager.GenerateEmailConfirmationTokenAsync(user);
And I get the following:
CfDJ8IjJLi0iO61KsS5NTyS4wJkSvCyzEDUBaVlXCkbxz6zwI1LocG8+WPubx5Rvoi4tFuiWAVFut4gfTnhgsdihE0gY+o7JyJrNtfXmzGLnczwbKZ3Wwy15+IUEi1h2qId72IRKvFqBSFv7rJdECSR/thZphpTQm7EnOuAA7loHlQFRWuMUVBce8HUsv1odbLNsKQ==
How can I create shorter tokens with a small lifespan instead of huge tokens?
If I understand the problem, you're looking at swapping out a TokenProvider, which can either be done at service container configuration stage
TokenProvider.cs
public class TokenProvider : IUserTwoFactorTokenProvider<IdentityUser>
{
public Task<string> GenerateAsync(string purpose, UserManager<IdentityUser> manager, IdentityUser user)
{
// generate your token here
}
public Task<bool> ValidateAsync(string purpose, string token, UserManager<IdentityUser> manager, IdentityUser user)
{
// validate your token here
}
public Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<IdentityUser> manager, IdentityUser user)
{
// check if user has email and it's been confirmed. or do your own logic
}
}
inject into your container at build time
services.AddIdentityCore<IdentityUser>(o =>
{
o.Tokens.EmailConfirmationTokenProvider = "MyTokenProvider";
}).AddEntityFrameworkStores<IdentityDbContext>()
.AddTokenProvider<TokenProvider>("MyTokenProvider");
or at run time:
_userManager.RegisterTokenProvider(um.Options.Tokens.ChangeEmailTokenProvider, new TokenProvider());
String token = await _userManager.GenerateEmailConfirmationTokenAsync(user);
there are a few token providers available to you by default (Email, PhoneNumber and Authenticator being some), which you can explore and build upon. As far as I can see the source, EmailTokenProvider defers actual code generation to TotpSecurityStampBasedTokenProvider which you can explore and see if your lifetime requirement can be changed by playing with the TOTP algorithm it implements
Lifespan doesn't factor in here either way. However, I think what you're actually talking about is an TOTP (timed one-time use password) - like the ones you get via SMS or an authenticator app. ASP.NET Core actually has TOTP providers built-in; they're just not used for things like email confirmation, password reset, etc. by default. However, that's easily changed:
services.Configure<IdentityOptions>(o =>
{
o.Tokens.EmailConfirmationTokenProvider = TokenOptions.DefaultEmailProvider;
});
Oddly enough, despite being called DefaultEmailProvider, that provider is not actually used by default for things like email confirmations. It's actually referring to being the default TOTP provider for 2FA codes delivered via email. Nevertheless, you can set it as the provider for email confirmation, as well.
I'm writing some REST api for my cake 3.0 application, and I need to set $this->Auth->unauthorizedRedirect to false, as the manual says that this would prevent my application to redirect to login url for unauthorized requests.
http://api.cakephp.org/3.0/class-Cake.Auth.BasicAuthenticate.html
The problem is that I'm trying to set it in my Users controller, and it doesn't work:
class UsersController extends AppController {
public function initialize() {
parent::initialize();
$this->loadComponent('RequestHandler');
}
public function beforeFilter(Event $event) {
parent::beforeFilter($event);
$this->Auth->allow(['logout']);
// Change the authentication mode when using REST api
if(! $this->RequestHandler->accepts('html')) {
$this->Auth->unauthorizedRedirect = false;
$user = $this->Auth->identify();
if ($user) {
$this->Auth->setUser($user);
}
}
}
This scripts works fine as detecting if a user is actually registered, but fails when I try to use wrong authentication data, showing the login form instead of throwing an error. What am I doing wrong?
Authentication and authorization are two different things
You are mixing up authentication and authorization, that's two different things. Logging in a user is authentication, testing whether a logged in user is allowed to access a specific action is authorization.
So the unauthorized redirect configuration applies to logged in users when accessing actions.
Handling unauthenticated requests
What you are looking for, ie throw an exception on unauthenticated requests, is done by the basic authentication adapter by default, so I assume that you actually aren't using this adapter!?
So if you are using a different adapter, this behavior is best implemented in either your controller where you are trying to identify the user
$user = $this->Auth->identify();
if (!$user) {
throw new ForbiddenException('Stop! Hammer time!');
} else {
$this->Auth->setUser($user);
}
or, in case you want the exception to be thrown for every controller, in a custom authentication adapters unauthorized() method, which is being invoked on unauthenticated requests before executing possible redirects. Quote from the docs:
Cookbook > Authentication > Handling Unauthenticated Requests
When an unauthenticated user tries to access a protected page first the unauthenticated() method of the last authenticator in the chain is called. The authenticate object can handle sending response or redirection by returning a response object, to indicate no further action is necessary. Due to this, the order in which you specify the authentication provider in authenticate config matters.
If authenticator returns null, AuthComponent redirects user to login action. [...]
Here's a simple example that extends the form authentication handler:
src/Auth/MyCustomAuthenticate.php
namespace App\Auth;
use Cake\Auth\FormAuthenticate;
use Cake\Network\Exception\ForbiddenException;
use Cake\Network\Request;
use Cake\Network\Response;
class MyCustomAuthenticate extends FormAuthenticate
{
public function unauthenticated(Request $request, Response $response)
{
if(!$request->accepts('text/html')) {
throw new ForbiddenException('Ah ah ah! You didn\'t say the magic word!');
}
}
}
Controller
$this->loadComponent('Auth', [
'authenticate' => [
'MyCustom'
]
]);
See also
Cookbook > Authentication > Creating Custom Authentication Objects
Cookbook > Authentication > Using Custom Authentication Objects
I'm using WebSecurity for Authentication and a custom ClaimsAuthorizationManager for Authorization.
Mixing Claims based authorization with the built in WebSecurity features for Authentication has provided me with a ton of value. I highly recommend it for anyone who requires complex authorization logic combining from several systems.
Anyways, everything is working great except the RememberMe feature.
When a user logs in, I set my auth cookie (via WebSecurity), new up my ClaimsPrincipal, and write it to my SessionSecurityToken. Bam, it works brilliantly.
However, when a user has previously elected to persist the (Websecurity) auth cookie, she is allowed to bypass my login method, which news up my ClaimsPrincipal and writes my principal to my SessionSecurityToken. My authorization fails because my claims haven't been loaded, because I haven't had a chance to transform my ClaimsPrincipal.
Is there a way to hook into a (Websecurity) "forms authentication cookie read" event? If so, I could handle it, new up my ClaimsPrincipal, and be on my way. Thanks in advance!
You could write a custom AuthorizeAttribute:
public class MyAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var authorized = base.AuthorizeCore(httpContext);
if (authorized)
{
httpContext.User = new ClaimsPrincipal(...)
}
return authorized;
}
}
Now decorate your protected controller actions with this custom attribute instead of the default built-in:
[MyAUthorize]
public ActionResult Protected()
{
...
}
Is there an alternative to the session feature plugin in servicestack? In some scenarios I cannot use cookies to match the authorized session in my service implementation. Is there a possibility to resolve the session using a token in http header of the request? What is the preferred solution for that in case the browser is blocking cookies?
I'm using ServiceStack without the built-in auth and session providers.
I use a attribute as request filter to collect the user information (id and token), either from a cookie, request header or string parameter.
You can provide this information after the user takes login. You append a new cookie to the response and inject the id and token info on clientside when rendering the view, so you can use for http headers and query parameters for links.
public class AuthenticationAttribute : Attribute, IHasRequestFilter
{
public void RequestFilter(IHttpRequest request, IHttpResponse response, object dto)
{
var userAuth = new UserAuth { };
if(!string.IsNullOrWhiteSpace(request.GetCookieValue("auth"))
{
userAuth = (UserAuth)request.GetCookieValue("auth");
}
else if (!string.IsNullOrEmpty(request.Headers.Get("auth-key")) &&
!string.IsNullOrEmpty(request.Headers.Get("auth-id")))
{
userAuth.Id = request.Headers.Get("id");
userAuth.Token = request.Headers.Get("token");
}
authenticationService.Authenticate(userAuth.Id, userAuth.token);
}
public IHasRequestFilter Copy()
{
return new AuthenticationAttribute();
}
public int Priority { get { return -3; } } // negative are executed before global requests
}
If the user isn't authorized, i redirect him at this point.
My project supports SPA. If the user consumes the API with xmlhttprequests, the authentication stuff is done with headers. I inject that information on AngularJS when the page is loaded, and reuse it on all request (partial views, api consuming, etc). ServiceStack is powerful for this type of stuff, you can easily configure your AngularJS app and ServiceStack view engine to work side by side, validating every requests, globalizing your app, etc.
In case you don't have cookies and the requests aren't called by javascript, you can support the authentication without cookies if you always generate the links passing the id and token as query parameters, and pass them through hidden input on forms, for example.
#Guilherme Cardoso: In my current solution I am using a PreRequestFilters and the built-in session feature.
My workflow/workaround is the following:
When the user gets authorized I took the cookie and send it to the client by using an http header. Now the client can call services if the cookie is set in a http-header (Authorization) of the request.
To achieve this I redirect the faked authorization header to the cookie of the request using a PreRequestFilter. Now I am able to use the session feature. Feels like a hack but works for the moment ;-)
public class CookieRestoreFromAuthorizationHeaderPlugin : IPlugin
{
public void Register(IAppHost appHost)
{
appHost.PreRequestFilters.Add((req, res) =>
{
var cookieValue = req.GetCookieValue("ss-id");
if(!string.IsNullOrEmpty(cookieValue))
return;
var authorizationHeader = req.Headers.Get("Authorization");
if (!string.IsNullOrEmpty(authorizationHeader) && authorizationHeader.ToLower().StartsWith("basictoken "))
{
var cookie = Encoding.UTF8.GetString(Convert.FromBase64String(authorizationHeader.Split(' ').Last()));
req.Cookies.Add("ss-id",new Cookie("ss-id",cookie));
req.Items.Add("ss-id",cookie);
}
});
}
}
I'm creating an app with MVC4 that will authorize users using Twitter and lets them tweet from the app as well. I'm able to get the user authenticated without a problem using the BuiltInOAuthClient.Twitter that is in MVC4. http://www.asp.net/web-pages/tutorials/security/enabling-login-from-external-sites-in-an-aspnet-web-pages-site
I have the access token, and oauth_verifier, but I need to get the acess_secret back from Twitter as well. https://dev.twitter.com/docs/auth/implementing-sign-twitter
What I'm missing is how to pass the oauth_verifier back to Twitter to get the access secret using OAuthWebSecurity.
Again, I can use Twitter for the login ok, but I need to be able to use twitter as the user as well. I've done this with the TweetSharp library before, but am trying to use DotNetOpenAuth on this project.
UPDATE:
I'm using the OAuthWebSecurity class as described in the first link to manage authentication. OAuthWebSecurity.RegisterClient in the AuthConfig expects a DotNetOpenAuth.AspNet.IAuthenticationClient. You can't swap that out with the TwitterConsumer class as suggested.
I can use the "built in" DotNetOpenAuth authentication piece as described in the first link, OR I can use custom code to do the full authorization, but I'm trying to find a way to do both.
I can do it separately, but then the user is presented with the Twitter dialog twice (once to login and once to authorize). I'm hoping there's a way to use the already wired up authentication piece that uses OAuthWebSecurity but ad the authorization piece as well.
I've been banging my head against a wall with this for a few days now, but I finally have something that works. Would be interested to know if it's a valid solution though!
First off, create a new OAuthClient:
public class TwitterClient : OAuthClient
{
/// <summary>
/// The description of Twitter's OAuth protocol URIs for use with their "Sign in with Twitter" feature.
/// </summary>
public static readonly ServiceProviderDescription TwitterServiceDescription = new ServiceProviderDescription
{
RequestTokenEndpoint =
new MessageReceivingEndpoint(
"https://api.twitter.com/oauth/request_token",
HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
UserAuthorizationEndpoint =
new MessageReceivingEndpoint(
"https://api.twitter.com/oauth/authenticate",
HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
AccessTokenEndpoint =
new MessageReceivingEndpoint(
"https://api.twitter.com/oauth/access_token",
HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement() },
};
public TwitterClient(string consumerKey, string consumerSecret) :
base("twitter", TwitterServiceDescription, consumerKey, consumerSecret) { }
/// Check if authentication succeeded after user is redirected back from the service provider.
/// The response token returned from service provider authentication result.
protected override AuthenticationResult VerifyAuthenticationCore(AuthorizedTokenResponse response)
{
string accessToken = response.AccessToken;
string accessSecret = (response as ITokenSecretContainingMessage).TokenSecret;
string userId = response.ExtraData["user_id"];
string userName = response.ExtraData["screen_name"];
var extraData = new Dictionary<string, string>()
{
{"accesstoken", accessToken},
{"accesssecret", accessSecret}
};
return new AuthenticationResult(
isSuccessful: true,
provider: ProviderName,
providerUserId: userId,
userName: userName,
extraData: extraData);
}
}
The important part is where you cast the response to an ITokenSecretContainingMessage. It appears that the response has the TokenSecret all along, but it is only on an internal property. By casting it, you get access to a public property. I can't say that I'm a fan of doing this, but then I also don't understand why DotNetOpenAuth the Asp.Net team have hidden the property in the first place. There must be a good reason.
You then register this client in AuthConfig:
OAuthWebSecurity.RegisterClient( new TwitterClient(
consumerKey: "",
consumerSecret: ""), "Twitter", null);
Now, in the ExternalLoginCallback method on the AccountController, the accessSecret is available in the ExtraData dictionary.
The DotNetOpenAuth.AspNet.Clients.TwitterClient class only allows authentication, not authorization. So you wouldn't be able to post tweets as that user if you use that class.
Instead, you can use DotNetOpenAuth.ApplicationBlock.TwitterConsumer, which does not share this limitation and you can even copy the source code for this type into your application and extend it as necessary.
You should be able to enhance the TwitterConsumer class (once you've copied it into your own project) to implement the required interface so that the OAuthWebSecurity class will accept it. Otherwise, you can just use TwitterConsumer directly yourself to both authenticate and authorize your web app so the user only sees Twitter once but you get all the control you need. After all, folks using ASP.NET have been using TwitterConsumer to both login and authorize for subsequent calls to Twitter for long before OAuthWebSecurity even existed.
For a WebForms project template which references Microsoft.AspNet.Membership.OpenAuth in AuthConfig.cs instead of Microsoft.Web.WebPages.OAuth (MVC4 Internet Application) I was able to modify Paul Manzotti's answer to get it to work:
Create a custom twitter client class that derives from DotNetOpenAuth.AspNet.Clients.TwitterClient
public class CustomTwitterClient : TwitterClient
{
public CustomTwitterClient(string consumerKey, string consumerSecret) :
base(consumerKey, consumerSecret) { }
protected override AuthenticationResult VerifyAuthenticationCore(AuthorizedTokenResponse response)
{
//return base.VerifyAuthenticationCore(response);
string accessToken = response.AccessToken;
string accessSecret = (response as ITokenSecretContainingMessage).TokenSecret;
string userId = response.ExtraData["user_id"];
string userName = response.ExtraData["screen_name"];
var extraData = new Dictionary<string, string>()
{
{"accesstoken", accessToken},
{"accesssecret", accessSecret}
};
return new AuthenticationResult(
isSuccessful: true,
provider: ProviderName,
providerUserId: userId,
userName: userName,
extraData: extraData);
}
}
Add the custom client in AuthConfig.cs
public static void RegisterOpenAuth()
{
OpenAuth.AuthenticationClients.Add("Twitter", () => new CustomTwitterClient(
consumerKey: ConfigurationManager.AppSettings["twitterConsumerKey"],
consumerSecret: ConfigurationManager.AppSettings["twitterConsumerSecret"]));
}
Ta-dow! Now you can haz access secret.
You can extract the oauth_token_secret from OAuthWebSecurity by designing your own TokenManager. You can register the token manager when you register your Twitter client in OAuthWebSecurity.RegisterClient.
I used this method to extract the needed values to be able to bypass the authorization step of the Linq-to-Twitter lib.
I will soon post my solution at my blog.