I just need a bit of direction here.
Want to migrate off our custom AuthZ implementation and on to leveraging what SS has.
Just need to know what SS pieces are to start with.
We have our own DNOA AuthZ server that serves oAuth2 access_token (in response to a client credentials flow - i.e. username password and clientid and clientsecret), and we include the produced access_token in each request to our SS resource service in an Authorization header as a Bearer token. Standard stuff right.
Our SS service then uses a custom RequestFilter called RequireAuthorizationAttribute configured on each service operation. This attribute uses DNOA (DNOA.StandardAccessTokenAnalyzer) to check the signature, unpack and verify the access-token, and extract the userId from it. We then stuff the userId into an IPrincipal to on the current thread identify the user throughout our API.
This is way overdue for us, but now I want to rip out and replace our RequireAuthorization attribute, and use whatever SS requires us to do to achieve essentially the same thing.
I am just not clear on what I need to do to replace this functionality, as my first goal.
My second goal, is actually to set us up for using other oAuth providers such as facebook, etc.
Could you confirm the steps I need to address here for this migration?
(I suspect, but don’t know for sure, that I will need to: (1) configure my service to use the AuthFeature() with some oAuth2 provider - but not sure which one. (2) use some SS attribute in place of our RequireAuthorization attribute to verify the bearer token. And (3) then I’ll need a different way to get access to the Id of the authorized user?)
Once I have all that going, supporting other oAuth2 providers will be a cinch, like described on the wiki.
Can you confirm this is what I should be doing, or not?
To implement authentication with you own oauth server you need to perform several steps:
Write auth provider CustomAuthenticationProvider derived from OAuthProvider. As a sample you can look at FacebookAuthProvider
You need to override Authenticate method to get the token from server, validate it, save into the session and mark session as authenticated. See sample code
If your Oauth server provides methods to get additional information about user, you can override LoadUserAuthInfo method in CustomAuthenticationProvider and get additional information from OAuth server, set tokens and then save it into the session in overrided LoadUserOAuthProvider.
See example code: LoadUserAuthInfo LoadUserOAuthProvider
Register AuthFeature plugin following the example from the wiki you mentioned above and use your CustomAuthenticationProvider as auth provider.
Plugins.Add(new AuthFeature(() => new AuthUserSession(),
new IAuthProvider[] {
new CustomAuthenticationProvider()
}));
Mark your services or DTOs with Authenticate attribute instead of RequireAuthorization attribute as shown here
If you need to add additional auth providers, you can add them in plugin registration code, for example:
Plugins.Add(new AuthFeature(() => new AuthUserSession(),
new IAuthProvider[] {
new CustomAuthenticationProvider(),
new FacebookAuthProvider(appSettings),
}));
To get information about your user Id and other user felds you can access AuthSession inside your seervices:
public class SecuredService : Service
{
public object Get(Secured request)
{
IAuthSession session = this.GetSession();
return new SecuredResponse() { Test = "You're" + session.FirstName };
}
}
ServiceStack doesn’t have its own OAuth Server implementation, so if you have an existing one you want to use you’ll still need to keep an instance of it and treat it like any other remote OAuth Server implementation where you can create a OAuth2 AuthProvider for it similar to ServiceStack’s other OAuth2 AuthProviders.
Personally I like using JWT’s instead, it’s simple, elegant, allows for greater flexibility and nicer client integration and is stateless where auth-enabled Servers don’t need to maintain access and configure their own centralized storage. The stateless feature is really nice which is something we take advantage in Gistlyn which supports both Web and Desktop clients without needing to maintain any db or session persistence yet still retains authentication across redeploys.
As it requires less maintenance, external dependencies whilst still maintaining uninterrupted redeployments we’ve also converted techstacks.io and servicestack.net to use JWT’s by making use of ServiceStack’s JWT Convert Session to Token feature to convert a users Authenticated Session into a JWT Token.
OK, apologies. I have entirely the wrong mental model here, which is not helping.
I need to learn the ServiceStack way now. We had previously been passing around access_token that clients have fetched from AuthZServer (and refreshed when needed) , and then used to access more than one service in our architecture, say Service A, Service B.
OK, moving on to doing this the ServiceStack way. Let’s say that I move my /auth endpoint to my main service (lets call that service: Service A). There I configure a JezzCustomOAuthProvider to talk to my custom AuthZ server called ‘JezzOAuthServer’ somewhere else on the internet. Now we have a /auth/jezz endpoint in service A that clients can use to authenticate.
A client sends a Authentication request to Service A (for provider Jezz). Service A, fires up the JezzCustomOAuthProvider instance, and that connects to the JezzOAuthZServer does the oAuth shuffle, and obtains an access_token and possibly provide some profile information for the user. Service A caches that access_token (presumably) and user info in some session variables, and returns a cookie to the client, who then stores that cookie for subsequent calls to Service A. Client then sends cookie in every call to Service A. Service A loads the user’s session in the service on each call. OK I get that conceptually.
So, what happens if the same client wants to connect to Service B (in the same architecture), that also trusts JezzOAuthServer. Can the client reuse the same session cookie to do that, or does the client HAVE to authenticate with service B separately?
What happens if Service A wants to call Service B (as the user), what does it use to do that?
Have a look at the http://mvc.servicestack.net Live Demo which shows what’s happening behind the scenes when you authenticate with multiple OAuth providers. The bottom of the home page shows what’s stored in the database after you authenticate with each OAuth provider whilst the /session route, i.e. http://mvc.servicestack.net/api/session shows what’s stored in the Users Session (the Cache Entry ss-id/ss-pid Cookies point to).
Essentially as an Authenticated User authenticates with multiple Auth Providers the Auth Provider info (e.g. access_token) is merged into their UserAuth/UserAuthDetails DB Tables and their AuthUserSession that’s stored in the registered caching provider.
A User is Authenticated if their ss-id/ss-pid Cookies references and Authenticated Session, if Service A and Service B share the same domain and they’re both configured to use the same remote Caching Provider then the same cookie will get sent when accessing both Service A and Service B which will resolve to an Authenticated Session.
If they’re not on the same domain, the same cookie wont be sent so the client will need to authenticate with each service individually, likewise if they’re not configured to reference the same caching provider the Cookie wont reference an Authenticated Session in both services.
(I think I am good now, but for others trying to grapple with moving to ServiceStack, a visual description of how the Authentication/Authorization mechanisms work between your service and the various social and custom services would be useful to document, so people can see what is going over the wire and what is being stored where.) The wiki does not really touch on that right now. It explains how, not what.
One last thing. We already have 3rd party partners that are obtaining an access_token from our custom ‘OAuthZ2Server’, and then calling our API and including the access_token as a Bearer in the Authorization HTTP header. They are not using our client to do that.
Even if we move to use AuthFeature and a custom OAuth2Provider in ServiceA, and effectively start using ss cookies and what not between our clients and services. What component is going to process the legacy access_token in the Authorization header when our partners make an API call?
The access_token isn’t looked at after the User is Authenticated with the original OAuth Provider, after the User’s Authenticated Session is setup and referenced using the ss-id/pid cookies, then each subsequent request just verifies the User has an Authenticated Session.
The Users Session contains the OAuth information collected after the client successfully authenticates with OAuth, but ServiceStack itself doesn’t look at it after they’re authenticated.