Please note that normally you’d inherit an existing AuthProvider like CredentialsAuthProvider, this makes sure that the necessary Auth events that’s in OnAuthenticated()
are called. Overriding Authenticate()
effectively replaces the built-in implementation so it’s an advanced feature where you need to know what you’re doing. Your default implementation has a lot missing, normally you’d have a Username or Email, Password and UserAuthId.
The issue here is that your custom HelloAuthProvider
is incompatible with the default GenerateNewSessionCookiesOnAuthentication behavior.
public class HelloAuthProvider : AuthProvider
{
private readonly ConcurrentDictionary<string, bool> _sessions = new ConcurrentDictionary<string, bool>();
public HelloAuthProvider()
{
Provider = "apphost";
AuthRealm = "/auth/" + Provider;
}
public override object Authenticate(IServiceBase authService, IAuthSession session, Authenticate request)
{
Console.WriteLine($$"Authenticate called: session.IsAuthenticated = {session.IsAuthenticated}");
session.IsAuthenticated = true;
_sessions[session.Id] = true;
return new AuthenticateResponse
{
SessionId = session.Id
};
}
public override bool IsAuthorized(IAuthSession session, IAuthTokens tokens, Authenticate request = null)
{
return session?.Id != null && _sessions.ContainsKey(session.Id);
}
}
You’re using the session id to determine whether a session is authenticated, but by the time the [Authenticate]
calls IsAuthorized()
to verify the Session, the cookies/sessionId have already been regenerated where the sessionId that was authenticated is no longer the session that’s being validated. So this logic is incompatible with GenerateNewSessionCookiesOnAuthentication
which you need to disable in order to get this to verify + validate successfully, i.e:
Plugins.Add(new AuthFeature(() => new AuthUserSession(), new IAuthProvider[] {
new HelloAuthProvider(),
}) {
GenerateNewSessionCookiesOnAuthentication = false,
});
If you changed it to use the built-in CredentialsAuthProvider
by adding it to AuthFeature, e.g:
public override void Configure(Funq.Container container)
{
container.Register<IAuthRepository>(c =>
new InMemoryAuthRepository());
Plugins.Add(new AuthFeature(
() => new AuthUserSession(),
new IAuthProvider[]
{
new HelloAuthProvider(),
new CredentialsAuthProvider(),
})
{
IncludeRegistrationService = true,
});
}
Then moving the HelloService
outside of the Program class so it can be reused, e.g:
[Route("/hello/{Name}")]
public class Hello : IReturn<HelloResponse>
{
public string Name { get; set; }
}
public class HelloResponse
{
public string Result { get; set; }
}
[Authenticate]
public class HelloService : Service
{
public object Any(Hello request)
{
var session = base.SessionAs<AuthUserSession>();
return new HelloResponse {
Result = "Hello, " + request.Name + ", sessionId: " + session.Id
};
}
}
class Program { ... }
Will let you re-use the DTOs on the client, I’ve created a new client project that registers a new User and authenticates + calls the authenticated Hello Service 5 times in a row like your example, e.g:
static void Main(string[] args)
{
var client = new JsonServiceClient("http://127.0.0.1:1337/");
client.Post(new Register
{
FirstName = "Test",
LastName = "User",
UserName = "test",
Password = "password",
AutoLogin = false,
});
for (int i = 0; i < 5; i++)
{
Console.WriteLine("Round {0}", i);
try
{
client.Post(new Authenticate
{
provider = "credentials",
UserName = "test",
Password = "password"
});
var response = client.Get(new Hello { Name = $$"Round {i}" });
Console.WriteLine("Success: " + response.Result);
}
catch (Exception ex)
{
Console.WriteLine("Failed with {0}", ex);
}
}
Console.ReadLine();
}
Which returns the expected response where each request is successful with a new sessionId:
Round 0
Success: Hello, Round 0, sessionId: QgW06KNY6qiTTLSCUI2Q
Round 1
Success: Hello, Round 1, sessionId: eENayX8mH2ibc2CBzMZ7
Round 2
Success: Hello, Round 2, sessionId: EAElu68VP7hUccxwS8qh
Round 3
Success: Hello, Round 3, sessionId: KO6KWZAHpCQqIpCix6M1
Round 4
Success: Hello, Round 4, sessionId: CW5yBs6ell9CoLST0UYY