I’m working with SS 5.9.2
My backend is a self hosted application
I need to expose an authenticate endpoint to allow a remote client to login
The remote client is legacy and can send the authentication request only to
https://server:port/public/auth/login
In my backend I’m using the CredentialsAuthProvider and I would like the overwrite the SS default login endpoint auth/credentials with the mine
Does SS allow to do this?
I’m using this code to set my endpoint to the Authenticate dto but the client reports NotFound
The issue will be the {provider} needs to be populated with the provider used to authenticate the request, either by the client sending it via the queryString e.g. ?provider=credentials.
Alternatively you can register the route with a “login” Auth Provider by registering the route:
Before to continue I need to clarify a little bit the context
the legacy client implements the jwt provider and it expects my SS server follows his implementation
the client posts to /public/auth/login an empty body with header Authorization Basic username:password and expects the following DTO { token: string, refresh: string, expireDate: datetime }
my SS server answers the expected DTO populating the token with CreateJwtBearerToken and the refresh with CreateJwtRefreshToken
if I register the “login” provider I get this error No configuration was added for OAuth provider 'credentials'
this is how the AuthFeature is registered
var authFeature = new AuthFeature(() => new AuthUserSession(),
new IAuthProvider[]
{
jwtProvider,
authProvider
})
{
ServiceRoutes = new()
{
{
typeof(AuthenticateService),
new[] {
"/public/auth/{provider}"
}
}
},
AllowGetAuthenticateRequests = req => false, // avoid the GET
IncludeAssignRoleServices = false, // no roles
IncludeAuthMetadataProvider = false,
IncludeDefaultLogin = false // no ss login form
};
Plugins.Add(authFeature);
jwtProvider is my custom JwtAuthProvider
authProvider is my custom CredentialsAuthProvider
I tried to register “login” in both jwtProvider and authProvider but I get the error above
I’m confused, so this is not an existing CredentialsAuthProvider request? You should only be posting a ‘credentials’ Username/Password request to a CredentialsAuthProvider.
As for the error, it’s because the request doesn’t match a registered Auth Provider, I’ve just noticed ServiceStack already has an alias for the login provider so you shouldn’t need to change the Auth Provider name and can leave it as ‘credentials’, but it’s not clear this is even a Custom Credentials Provider?
If this was an existing custom JwtAuthProvider than you should just reuse whatever Auth Provider you’ve implemented previously. But if it was a JWT provider, I don’t get why it would be sending/expecting an Authorization Basic request? Seems it some heavily customized Auth Provider in which case I’m not going to be able to help with your custom impl.
Either way if this was an existing ServiceStack Auth Provider I would recommend that you reuse what you had that worked.
The current backend has no authentication.
Now I have to add the authentication feature to allow the legacy client to use the backend.
The client Posts the BasicAuthorization header to /public/auth/login but the header value is encrypted by a legacy client algo implementation.
I’m using a custom CredentialAuthProvider and the custom Authenticate method to decrypt the Authorization header value and to authenticate the user.
The client expects to get a DTO with a token, a refreshToken and an expiryDate.
I’m using SS JwtAuthProvider to get these fields avoiding to implement again what JwtAuthProvider does.
Finally the client is able to successfully call the backend Services with the attribute [Authenticate("jwt")].
Later I’ve discovered that SS does not distinguish between access and refresh token.
For SS both tokens are valid, so I’m using a custom JwtAuthProvider and the custom CreateSessionFromPayload method to identify the access token and reject the refresh token because the refresh token can only be used to get a new token and not to run the Service logics.
This is why I’m registering the CredentialAuthProvider and the JwtAuthProvider (both custom implementations).
if you have any suggestions to implement the simpler things please let me know.
The last thing.
The client Posts the BasicAuthorization header to /public/auth/refresh to refresh the token.
Does this endpoint clashes with some SS built-in routes?
They’re both JWTs but their payloads are completely different, where only JWT access tokens (typ=JWT) contains partial session information that the request will be authenticated with, not the refresh token (typ=JWTR). You should be able to view the contents of each on https://jwt.io
If you don’t need any other Auth Providers for this app you should be able to register just 1 Auth Provider since if {provider} is empty it will default to the first registered Auth Provider.
But given this requires a custom implementation I’d personally only inherit the AuthProvider base class and copy any functionality you need from the JWT Auth Provider classes.
routes starting with /auth are used by the AuthFeature, there’s no built-in routes starting with /public.