In my request filter I'm setting some properties in a custom session which I later access from the service. This works as expected.
Request Filter:
public sealed class CustomAttribute:RequestFilterAttribute
{
public override void Execute(IRequest req, IResponse res, object requestDto)
{
var session = req.SessionAs<CustomSession>();
if (!session.FromToken)
{
throw new AuthenticationException("Please authenticate using bearer token or with flag 'UseTokenCookie' set to true.");
}
... do some work ...
session.X = tup.Item2;
session.Y = tup.Item1;
req.SaveSession(session);
}
}
In my service:
var session = this.SessionAs<CustomSession>();
var myX = session.X;
var myY = session.Y;
... do some work ...
var someObj = new MyOtherClass();
someObj.DoSomeWork();
Later in the same request, I tried to access these same properties and was returned a different session.
public class MyOtherClass
{
...stuff...
public void DoSomeWork()
{
...
var req = HostContext.AppHost.TryGetCurrentRequest();
var session = req.SessionAs<CustomSession>(); //-> this returns a new session?
var myX = session.X; //-> so this is null
var myY = session.Y; //-> and this is null
}
}
My question is why? It's the same request. What am I doing wrong?
For context - I'm using JWT (as of 4.5.6) and 'MyOtherClass' is actually a singleton error handling class which decides when a failure is significant enough to fail a transaction or trigger an email notification.
You're not accessing the same IRequest instance when you use HostContext.TryGetCurrentRequest(), it creates a new instance for the ASP.NET Request which needs to re-fetch the session from the cache.
You'll either need to pass the same base.Request instance in your Service (recommended) which will let you access the same locally-cached session instance or you can save the session after you make changes using IRequest.SaveSession() that way when the session is re-fetched it will load the modified session. If you're using the default MemoryCacheClient you'll incur no I/O costs.
Related
I am pretty new to this.. so any help would be greatly appreciated.
I have a WebApi service that uses OAUTH token and refresh token authentication.
All works well at the moment:
step1: I send in the user and password and it generates an authentication token and a refresh token. The refresh token is saved in the DB.
step2. I can now use the refresh token and i receive the authentication token and a new refresh token. I want a way to use the same refresh token i sent and not reuse a new one.
This is my code for the refresh token:
public class SimpleRefreshTokenProvider : IAuthenticationTokenProvider
{
public async Task CreateAsync(AuthenticationTokenCreateContext context)
{
RefreshTokensRepository _repo = new RefreshTokensRepository();
var clientid = context.Ticket.Properties.Dictionary["as:client_id"];
//HERE I regenerate the token, but I have no idea how to retrieve the already sent one.
var refreshTokenId = Guid.NewGuid().ToString("n");
//saving in BD:
var refreshTokenLifeTime = context.OwinContext.Get<string>("as:clientRefreshTokenLifeTime");
var token = new RefreshTokens()
{
Id = Helper.GetHash(refreshTokenId),
ClientId = clientid,
Subject = context.Ticket.Identity.Name,
IssuedUtc = DateTime.UtcNow,
ExpiresUtc = DateTime.UtcNow.AddMinutes(Convert.ToDouble(refreshTokenLifeTime))
};
context.Ticket.Properties.IssuedUtc = DateTime.UtcNow;
context.Ticket.Properties.ExpiresUtc = DateTime.UtcNow.AddMinutes(Convert.ToDouble(refreshTokenLifeTime));
token.ProtectedTicket = context.SerializeTicket();
var result = _repo.Add(token);
if(!string.IsNullOrEmpty(result))
context.SetToken(refreshTokenId);
}
public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin");
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
string hashedTokenId = Helper.GetHash(context.Token);
RefreshTokensRepository _repo = new RefreshTokensRepository();
var refreshToken = _repo.FindById(hashedTokenId);
if (refreshToken != null)
{
//Get protectedTicket from refreshToken class
context.DeserializeTicket(refreshToken.ProtectedTicket);
_repo.Remove(hashedTokenId);
}
}
void IAuthenticationTokenProvider.Create(AuthenticationTokenCreateContext context)
{
throw new NotImplementedException();
}
void IAuthenticationTokenProvider.Receive(AuthenticationTokenReceiveContext context)
{
throw new NotImplementedException();
}
}
My code is based on this samples:
http://bitoftech.net/2014/07/16/enable-oauth-refresh-tokens-angularjs-app-using-asp-net-web-api-2-owin/
I would like to use the same sent refresh token, but I have no idea how to use the already sent one in this context.
Any ideas?
Disclaimer: I don't condone reusing refresh tokens.
However, this does provide a good opportunity for everyone to improve knowledge of how this process works and there could be a good reason for reusing past refresh tokens in certain scenarios. I'm basing my answer upon:
Question: "I want a way to use the same refresh token i sent and not reuse a new one."
Code comment, "//HERE I regenerate the token, but I have no idea how to retrieve the already sent one."
PseudoCode Steps:
Store a user identifier as a property in AuthenticationProperties in the GrantResourceOwnerCredentials() method. From the sample code, it looks like you may already be doing this with "userName":
var props = new AuthenticationProperties(new Dictionary<string, string>
{
{
"as:client_id", (context.ClientId == null) ? string.Empty : context.ClientId
},{
"userName", context.UserName
}
});
Retrieve the user identifier in the CreateAsync() method of your IAuthenticationTokenProvider implementation (e.g. "SimpleRefreshTokenProvider" in your case). This would look something like:
public async Task CreateAsync(AuthenticationTokenCreateContext context)
{
var userName = context.Ticket.Properties.Dictionary["userName"];
...
Still in the CreateAsync() method use the user identifier to lookup the existing refresh token. This would look something like:
var existingRefreshToken = await _repo.FindRefreshTokenByUserNameAsync(userName);
Note: You would need to write the above method into your AuthRepository class from the example code. The "FindRefreshTokenByUserNameAsync(userName) implementation might include something like this if you're using Entity Framework and have a "RefreshToken" table that is being used to persist the granted refresh token:
var existingToken = RefreshToken.Where(r => r.UserName == userName).SingleOrDefault();
At this point, you have the existing token and should be able to re-use that refresh token value instead of Guid.NewGuid():
var refreshTokenId = existingToken.Token;
Taking a look at the tutorial's example code, however, indicates that a HashAlgorithm is being used to store the refresh token's value. That could complicate things a bit for you as storing a hash value is better security, but the process of hashing here is meant to be one-way.
If you really want to reuse the original token value when all you have persisted is the hashed token, would need to implement code that captures the non-hashed token value in the ReceiveAsync() method. It would have to temporarily persist the non-hashed value long enough for you to use it in the CreateAsync() method. In other words, you would have to save/persist the "context.Token" in ReceiveAsync(), associate it with your userName (from context.Ticket.Properties.Dictionary["userName"]), and use it later in the CreateAsync() method. It's hacky and I don't like it, but you would do it around this line of code in ReceiveAsync():
string hashedTokenId = Helper.GetHash(context.Token);
I have implemented a custom AuthenticateAttribute, AuthUserSession and CredentialsAuthProvider. In the Execute method of my AuthenticateAttribute I do:
public override void Execute(IRequest request, IResponse response, object requestDto)
{
var session = request.GetSession() as IMyCustomAuthUserSession;
// Copy certain request headers into a dictionary on my session object
}
I need to store certain special headers that are sent to me for later use. This works correctly when authentication is not enabled. When authentication IS enabled and the user has to log in, the TryAuthenticate method of my CredentialsAuthProvider class fires:
public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
var session = authService.GetSession() as IMyCustomAuthUserSession;
}
The sessions in these to methods are not the same since the session I get in the TryAuthenticate method - which fires after the AuthenticateAttribute.Execute method - does not contain the headers I stored there.
The special headers are only sent in the very first call to the web server so I need to get them into the new session of the TryAuthenticate method.
How can I do this?
Passing Session data between logins is going to be difficult as Sessions are invalidated between Authentication attempts. You can choose to retain the same Session Cookies between logins by configuring the AuthFeature plugin with:
Plugins.Add(new AuthFeature(...) {
GenerateNewSessionCookiesOnAuthentication = false
});
Which will retain the same users ss-id/ss-pid cookies on Login.
Use SessionBag for persisting Data between Auth Sessinos
For persisting data outside of an Authenticated User Session you can use a SessionBag, e.g:
//Save
base.SessionBag["cart"] = new Cart { ... };
//Retrieve
var cart = base.SessionBag.Get<Cart>("cart");
Persisting User Data under a Custom Cookie
An alternative solution is to persist data under a Custom Cookie, that way it wont get invalidated by ServiceStack during Authentication.
Where you can register a Global Request Filter to ensure each client/browser has a custom Cookie Id, e.g:
GlobalRequestFilters.Add((req,res,dto) => {
var uniqueId = SessionExtensions.CreateRandomSessionId();
var httpRes = res as IHttpResponse;
httpRes.Cookies.AddPermanentCookie("my-id", uniqueId);
req.Items["my-id"] = uniqueId; //if also needed for this request
});
Then on subsequent requests you can persist data under your unique Cookie Id, e.g:
var uniqueId = req.GetSessionParam("my-id");
var cacheKey = $"urn:Cart:{uniqueId}";
var cache = req.GetCacheClient();
cache.Set(cacheKey, new Cart { ... });
Then later retrieve it with:
var uniqueId = req.GetSessionParam("my-id");
var cacheKey = $"urn:Cart:{uniqueId}";
var cache = req.GetCacheClient();
var cart cache.Get<Cart>(cacheKey);
The context of this post involves ASP.NET Web API 2.2 + OWIN
The environment is a single application with both OWIN server and Web Api.
Background:
In the Startup class, one must specify OAuthBearerServerOptions which is supplied to the OAuthBearerAuthenticationProvider. These options are created during the start up of the OWIN server. On the OAuthBearerServerOptions, I must specify the AccessTokenExpireTimeSpan so that I can ensure expiry of tokens.
The Issue
I must be able to dynamically specify the Expiration time span on a per authentication request basis. I am unsure if this can be done and was wondering:
Can it be done?
If yes; at which point could I perform this look up and assignment of the expiration?
Content of start up config:
var config = new HttpConfiguration();
WebApiConfig.Register(config);
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
var OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/OAuth"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(**THIS NEEDS TO BE DYNAMIC**)),
Provider = new AuthorizationServerProvider()
};
//STOP!!!!!!!!
//DO NOT CHANGE THE ORDER OF THE BELOW app.Use statements!!!!!
//Token Generation
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll); //this MUST come before oauth registration
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()
{
Provider = new BearerProvider()
});
app.UseAutofacMiddleware(container); //this MUST come before UseAutofacWebApi
app.UseAutofacWebApi(config);//this MUST come before app.UseWebApi
app.UseWebApi(config);
I started messing with the BearerProvider class (see app.UseOAuthBearerAuthentication above for where I use this class) and in specific, the ValidateIdentity method, but wasn't sure if that was the proper point in the auth workflow to set this value. It seemed appropriate, but I seek validation of my position.
public class BearerProvider : OAuthBearerAuthenticationProvider
{
public override async Task RequestToken(OAuthRequestTokenContext context)
{
await base.RequestToken(context);
//No token? attempt to retrieve from query string
if (String.IsNullOrEmpty(context.Token))
{
context.Token = context.Request.Query.Get("access_token");
}
}
public override Task ValidateIdentity(OAuthValidateIdentityContext context)
{
//context.Ticket.Properties.ExpiresUtc= //SOME DB CALL TO FIND OUT EXPIRE VALUE..IS THIS PROPER?
return base.ValidateIdentity(context);
}
}
Thanks in advance!
Setting context.Options.AccessTokenExpireTimeSpan will actually change the global value, and affect all requests, that won't work for the original requirement.
The right place is the TokenEndpoint method.
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
{
...
if (someCondition)
{
context.Properties.ExpiresUtc = GetExpirationDateFromDB();
}
...
}
So I was in the wrong spot entirely. What I ended up having to do was to use my custom OAuthorizationServerProvider and in the overridden GrantResourceOwnerCredentials method in that custom class, I was able to set the timeout value by accessing the...
context.Options.AccessTokenExpireTimeSpan
property.
<!-- language: c# -->
public class AuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
//DO STUFF
var expireValue=GetTimeOutFromSomeplace();
context.Options.AccessTokenExpireTimeSpan = expireValue;
//DO OTHER TOKEN STUFF
}
}
I using ServiceStack authentication with a custom session object. I've got everything set up with different authentication providers and everything is working fine.
Now a want to store some information in the session before the user is authenticated (Think shopping cart). But we loose that information when the user logs in later. Looking at the code in the documentation this makes sense:
Plugins.Add(new AuthFeature(() => new AuthUserSession(),
new IAuthProvider[] {
new BasicAuthProvider(), //Sign-in with Basic Auth
new CredentialsAuthProvider(), //HTML Form post of UserName/Password credentials
}));
The authentication removes the existing session whenever a user logs in. This makes sense when the old login is a valid user, you want to make sure it's fully logged out. However when the current session isn't authenticated there doesn't seem to be much reason to do so.
I've been looking at a custom session factory, but that doesn't help me because as () => new AuthUserSession() shows, there isn't any context to use when creating the new session. Without a way to get the old session there I've got no way to copy any information.
I can work around it by overriding AuthProvider.Authenticate() and grab the required information before calling base. But that means doing so in every authentication provider we use and the ones we might use in the future. That doesn't really feel like the correct solution.
Is there a cleaner way to carry information across the authentication? Preferably something which works regardless of the AuthProvider used.
Whilst the Typed Sessions are re-created after authenticating, the Permanent and Temporary Session Ids themselves remain the same which lets you use ServiceStack's dynamic SessionBag to store information about a user which you can set in your Services with:
public class UnAuthInfo
{
public string CustomInfo { get; set; }
}
public class MyServices : Service
{
public object Any(Request request)
{
var unAuthInfo = SessionBag.Get<UnAuthInfo>(typeof(UnAuthInfo).Name)
?? new UnAuthInfo();
unAuthInfo.CustomInfo = request.CustomInfo;
SessionBag.Set(typeof(UnAuthInfo).Name, unAuthInfo);
}
}
You can then access the dynamic Session Bag in your Custom AuthUserSession Session Events with:
public class CustomUserSession : AuthUserSession
{
[DataMember]
public string CustomInfo { get; set; }
public override void OnAuthenticated(IServiceBase service, IAuthSession session,
IAuthTokens tokens, Dictionary<string, string> authInfo)
{
var sessionBag = new SessionFactory(service.GetCacheClient())
.GetOrCreateSession();
var unAuthInfo = sessionBag.Get<UnAuthInfo>(typeof(UnAuthInfo).Name);
if (unAuthInfo != null)
this.CustomInfo = unAuthInfo.CustomInfo;
}
}
New Session API's in v4.0.32+
Accessing the Session bag will be a little nicer in next v4.0.32+ of ServiceStack with the new GetSessionBag() and convenience ISession Get/Set extension methods which will let you rewrite the above like:
public object Any(Request request)
{
var unAuthInfo = SessionBag.Get<UnAuthInfo>() ?? new UnAuthInfo();
unAuthInfo.CustomInfo = request.CustomInfo;
SessionBag.Set(unAuthInfo);
}
//...
public override void OnAuthenticated(IServiceBase service, IAuthSession session,
IAuthTokens tokens, Dictionary<string, string> authInfo)
{
var unAuthInfo = service.GetSessionBag().Get<UnAuthInfo>();
if (unAuthInfo != null)
this.CustomInfo = unAuthInfo.CustomInfo;
}
I currently have an IIS hosted application that I would like to switch over to use the self-hosted method.
But I'm having difficulty accessing the session so I can retrieve the current users username.
This is the code I used when hosting under IIS which worked perfectly:
/// <summary>
/// A basic wrapper for the service stack session, to allow access to it lower down in the DAL layer without tying us to servicestack.
/// </summary>
public class ServiceStackAuthTokenService : IAuthTokenService
{
/// <summary>
/// GetCurrentAuthToken.
/// </summary>
/// <returns>A string representing the users auth name.</returns>
public string GetCurrentAuthToken()
{
// Grab the current request.
var req = HttpContext.Current.Request.ToRequest();
var res = HttpContext.Current.Response.ToResponse();
// Fetch the authentication service.
var authService = EndpointHost.AppHost.TryResolve<AuthService>();
authService.RequestContext = new HttpRequestContext(req, res, null);
// Grab the session.
var session = authService.GetSession(false);
// Return the username.
return session.UserName;
}
public string UserPropertyName
{
get { return "UserName"; }
}
}
This is added to the app host with the following code::
container.RegisterAutoWiredAs<ServiceStackAuthTokenService, IAuthTokenService>()
When running self-hosted the HttpContext.Current is null, how do I access the request under a self-hosted application?
Thanks!
Update
Additional things I have tried:
as per an post here: https://groups.google.com/forum/#!msg/servicestack/jnX8UwRWN8A/_XWzTGbnuHgJ
It was suggested to use:
container.Register>(c => AuthService.CurrentSessionFactory);
This just returns a newed IAuthSession.
What the user in that post is doing is exactly what I'm trying to achieve.
In the last post Mythz says:
Just to be clear, in order to form the Session Key that references the Users session you need either the ss-id or ss-pid cookies (as determined by ss-opts).
You can get cookies off the IHttpRequest object or otherwise in ASP.NET the HttpContext.Current.Request singleton, so whatever IAuthUserSession factory you inject needs to take something that can give it the cookies, i.e. either an IRequestContext, IHttpRequest, IService, etc.
But I still cant see a way to access the IHttpRequest.
For ServiceStack 3, you can share request data via the HostContext.Instance.Items Dictionary. For ServiceStack 4, you should use the HostContext.RequestContext.Items Dictionary.
For example, add a request filter in your app host configuration to save the value:
// Put the session into the hostcontext.
RequestFilters.Add((req, res, requestDto) =>
{
HostContext.Instance.Items.Add("Session", req.GetSession());
});
Then in your authentication token class pull it back out:
public string GetCurrentAuthToken()
{
var session = HostContext.Instance.Items["Session"] as AuthUserSession;
if (session != null)
{
return session.UserName;
}
throw new Exception("No attached session found.");
}