I have a WCF service where I use a customUserNamePasswordValidatorType (specified in the behaviors\serviceBehaviors\serviceCredentials\userNameAuthentication section of the web.config file).
My custom UserNamePasswordValidator works that way:
public bool Authenticate(string userName, string password)
{
If ( IsUserValid(username, password) )
{
UserInfo currentUser = CreateUserInfo(username);
//
// Here I'd like to store the currentUser object somewhere so that
// it can be used during the service method execution
//
return true;
}
return false;
}
During the service call execution, I need to access the info of the authenticated user. For instance I would like to be able to implement:
public class MyService : IService
{
public string Service1()
{
//
// Here I'd like to retrieve the currentUser object and use it
//
return "Hello" + currentUser.Name;
}
}
My question is how and where should I store the information during the authentication process so that it can be accessed during the call execution process? That storage should only last as long as the "session" is valid.
By the way, I don't use (and don't want to use) secure sessions and/or reliable sessions. So I have both establishSecuritytContext and reliableSessions turned off.
I'm thinking of enabling ASP.NET Compatibility Mode to store the user info in the HttpContext.Current.Session but I have the feeling it's not how it should be done.
Store anything that needs to be persisted into a persistant store - e.g. a database, that's the best way to go.
Store the user info in a user table, e.g. the ASP.NET membership system or something of your own. Keep some kind of a identifying token (username, ID etc.) at hand to retrieve that info from the database when needed.
You should strive to have a stateless WCF service whenever possible - it should never depend on a "state" of any kind other than what's safely stored in a database.
Related
My environment is ASP.NET Core 2.x accessing CosmosDb (aka DocumentDb) with the .NET SDK.
The default consistency level of my collection is set to "Session". For my use-case I need a single authenticated web user to always have consistent data in terms of reads/writes between web requests.
I have some CosmosDB Repository logic that is made available to my controller logic via ASP.NET Core Singleton dependency injection as such:
services.AddSingleton<DocumentDBRepository, DocumentDBRepository>(x =>
new DocumentDBRepository(
WebUtil.GetMachineConfig("DOCDB_ENDPOINT", Configuration),
WebUtil.GetMachineConfig("DOCDB_KEY", Configuration),
WebUtil.GetMachineConfig("DOCDB_DB", Configuration),
"MyCollection",
maxDocDbCons));
DocumentDBRespository creates a cosmos client like so:
public DocumentDBRepository(string endpoint, string authkey, string database, string collection, int maxConnections)
{
_Collection = collection;
_DatabaseId = database;
_Client = new DocumentClient(new Uri(endpoint), authkey,
new ConnectionPolicy()
{
MaxConnectionLimit = maxConnections,
ConnectionMode = ConnectionMode.Direct,
ConnectionProtocol = Protocol.Tcp,
RetryOptions = new RetryOptions()
{
MaxRetryAttemptsOnThrottledRequests = 10
}
});
_Client.OpenAsync().Wait();
CreateDatabaseIfNotExistsAsync().Wait();
CreateCollectionIfNotExistsAsync().Wait();
}
As far as I understand that means one CosmosDB client per Web App server. I do have multiple web app servers, so a single user might hit the CosmosDB from multiple AppServers and different CosmosDb clients.
Before a user interacts with the ComosDB, I check their session object for a CosmosDb SessionToken, like so:
string docDbSessionToken = HttpContext.Session.GetString("StorageSessionToken");
Then, when writing a document for example, the method looks something like so:
public async Task<Document> CreateItemAsync<T>(T item, Ref<string> sessionTokenOut, string sessionTokenIn = null)
{
ResourceResponse<Document> response = null;
if (string.IsNullOrEmpty(sessionTokenIn))
{
response = await _Client.CreateDocumentAsync(UriFactory.CreateDocumentCollectionUri(_DatabaseId, _Collection), item);
}
else
{
response = await _Client.CreateDocumentAsync(UriFactory.CreateDocumentCollectionUri(_DatabaseId, _Collection), item, new RequestOptions() { SessionToken = sessionTokenIn });
}
sessionTokenOut.Value = response.SessionToken;
Document created = response.Resource;
return created;
}
The idea being that if we have a session token, we pass one in and use it. If we don't have one, just create the document and then return the newly created session token back to the caller. This works fine...
Except, I'm unclear as to why when I do pass in a session token, I get a DIFFERENT session token back. In other words, when _Client.CreateDocumentAsync returns, response.SessionToken is always different from parameter sessionTokenIn.
Does that mean I should be using the new session token from that point on for that user? Does it mean I should ignore the new session token and use the initial session token?
How long do one of these "sessions" even last? Are they sessions in the traditional sense?
Ultimately, I just need to make sure that the same user can always read their writes, regardless of which AppServer they connect with or how many other users are currently using the DB.
I guess the confusion here is on what a session is?
In most scenarios/frameworks treat session as static identifier (correlation), where as with cosmos the sessionToken is dynamic (kind of bookmark/representation of cosmos db state, which changes with writes). Naming it as 'sessionToken' might be root of the confusion.
In this specific scenario, you should use the "returned sessiontoken" from cosmos API's.
Maybe I'm thinking about this wrong, but I'm trying to create a custom attribute for our CMS to handle auth checks.
https://gist.github.com/sitefinitysteve/62ab761256a64a84d8a6#file-sitefinityjwt-cs-L39
So if this service is called from within the CMS from a logged in user, user data is all there for the service method already.
But in the context of being called from an app, the user is technically Anonymous, however I can decode the token and get the user just fine... but not sure how to like pass that over to the service.
Am I just maybe looking at this wrong, and the proper thing to do is to call a CMS API method to just log that person in (seems slow if I already have the persons user object from line 33, and the service context expires instantly.
Use Request.Items Dictionary
You would use the IRequest.Items dictionary for any data you want to pass throughout ServiceStack's Request Pipeline:
//RequestFilter:
req.Items["info"] = new MyRequestInfo { ... };
In Service:
var info = (MyRequestInfo)base.Request.Items["info"];
Have DTO's share common interface
Another option for adding extra info to your Service is to have Request DTO's implement an interfaces, e.g:
public interface IHasInfo
{
MyRequestInfo Info { get; set; }
}
Which you could then populate in your Request Filter, e.g:
((MyRequestInfo)dto).Info = new MyRequestInfo { ... };
Access in Service like any other DTO property, e.g:
public object Any(Request request)
{
var info = request.Info;
}
I am trying to make a web app in Sencha Touch with Springboot as my back-end. My app is going to have users and each one of them is going to have their own separate activity. How do I make my app "know" what user is logged in so it can display their specific details? I am a newbie and don't know exactly how this needs to be done, especially on the server side (Springboot). If somebody could throw some light, that would be awesome! Thanks!
Assuming you are planning to use Spring Security, the current-user data can be obtained through its principal. There are a few ways to get the principal. One way is to have a principal parameter in the controller method, and Spring will inject it. Like this:
#RequestMapping(value = "/user", method = RequestMethod.GET)
#ResponseBody
public String currentUserName(Principal principal) {
return principal;
}
Another way would be to have a utility method like this:
public static User getUser() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null) {
Object principal = auth.getPrincipal();
if (principal instanceof User) {
return (U) principal;
}
}
return null;
}
This can then be called from the controller method.
I have a Website that contains a number of webpages and some WCF services.
I have a logging IHttpModule which subscribes to PreRequestHandlerExecute and sets a number of log4net MDC variables such as:
MDC.Set("path", HttpContext.Current.Request.Path);
string ip = HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
if(string.IsNullOrWhiteSpace(ip))
ip = HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"];
MDC.Set("ip", ip);
This module works well for my aspx pages.
To enable the module to work with WCF I have set aspNetCompatibilityEnabled="true" in the web.config and RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed on the service.
But when the service method is called the MDC no longer contains any of the set values. I have confirmed they are being set by putting a logging method in the PreRequestHandlerExecute.
I think the MDC is loosing the values because in the log I can see the PreRequestHandlerExecute handler method and service method calls are on separate
threads.
The post log4net using ThreadContext.Properties in wcf PerSession service suggests using log4net.GlobalContext but I think that solution would run into issues if two users hit the application at the same time as GlobalContext is shared by all threads.
Is there a way to make this work?
Rather than taking the values from the HttpContext and storing them in one of log4net's context objects, why not log the values directly from the HttpContext? See my answer to the linked question for some techniques that might work for you.
Capture username with log4net
If you go to the bottom of my answer, you will find what might be the best solution. Write an HttpContext value provider object that you can put in log4net's GlobalDiagnosticContext.
For example, you might do something like this (untested)
public class HttpContextValueProvider
{
private string name;
public HttpContextValueProvider(string name)
{
this.name = name.ToLower();
}
public override string ToString()
{
if (HttpContext.Current == null) return "";
var context = HttpContext.Current;
switch (name)
{
case "path":
return context.Request.Path;
case "user"
if (context.User != null && context.User.Identity.IsAuthenticated)
return context.User.Identity.Name;
case "ip":
string ip = context.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
if(string.IsNullOrWhiteSpace(ip))
ip = context.Request.ServerVariables["REMOTE_ADDR"];
return ip;
default:
return context.Items[name];
}
return "";
}
}
In the default clause I assume the name, if it is not a specifically case that we want to handle, represents a value in the HttpContext.Current.Items dictionary. You could make it more generic by also adding the ability to access Request.ServerVariables and/or other HttpContext information.
You would use this object like so:
Somewhere in your program/web site/service, add some instances of the object to log4net's global dictionary. When log4net resolves the value from the dictionary, it will call ToString before logging the value.
GDC.Set("path", new HttpContextValueProvider("path"));
GDC.Set("ip", new HttpContextValueProvider("ip"));
Note, you are using log4net's global dictionary, but the objects that you are putting in the dictionary are essentially wrappers around the HttpContext.Current object, so you will always be getting the information for the current request, even if you are handling simultaneous requests.
Good luck!
I am using ServiceStack's SocialBootstrapApi and it contains a class CustomUserSession that I can use to override the OnRegistered method. I want to override it because I am attempting to obtain information about the registration so that I can publish an event that a new user has registered. This handler provides an instance of the RegistrationService that handled the registration but not anything about the registration request itself or the resulting UserAuth instance. For instance, I'd like to get the e-mail address used to register.
public override void OnRegistered(IServiceBase registrationService)
{
base.OnRegistered(registrationService);
// Ideally, I could do get the registered user's primary e-mail address from the UserAuth instance.
var primaryEmail = ((RegistrationService) registrationService)
.UserAuthRepo
.GetUserAuth(this, null) //<--- 'this' is a mostly empty session instance
.PrimaryEmail;
}
This of course doesn't work because the session instance I'm using for the GetUserAuth call doesn't contain any of the necessary authentication information to be useful for looking up the user's authentication information. So GetUserAuth returns null as you would expect. So how should I go about obtaining this information? Would it be incorrect design for the OnRegistered handler to be passed the UserAuth instance created by the RegistrationService?
public interface IAuthSession
{
...
void OnRegistered(IServiceBase registrationService, UserAuth userAuth); // <-- new signature
...
}
That would be convenient! :)
Or perhaps there's another way to go about this?
Thanks in advance.
So how should I go about obtaining this information?
You should be able to access all the data of the Registration request via the registrationService. You just have to do a little digging and casting...
public override void OnRegistered(IServiceBase registrationService)
{
base.OnRegistered(registrationService);
var requestContext = (HttpRequestContext)registrationService.RequestContext;
var dto = ((Registration)requestContext.Dto);
var primaryEmail = dto.Email;
}
Would it be incorrect design for the OnRegistered handler to be passed the UserAuth instance created by the RegistrationService?
I'll leave design decisions to the professionals. The above code should work. The casting seems a bit ugly but all the necessary data is there.
I do not like hack into SS, so I chose to select user auth info from UserAuth collection by dto.UserName