Open dylanmorley opened 8 years ago
Working version now available on my fork - We went with allow_anonymous as the new scope,
To test, you can call from the Javascript Implicit client - Token Manager. Add another button with attributes data-scope='openid profile allow_anonymous' data-type='id_token token'
When you're not authenticated, you'll get the anonymous token back. When you are signed in, you'll get the authenticated token back.
thanks! We will look into it. Might take a couple of days because I am travelling next week.
No probs on timings, whenever you get a spare moment,
Some notes on how I expect this to work when you're reviewing,
That's pretty much it it terms of flow - let me know if any questions.,
Thanks
So it seems this all can be implemented using the standard extensibility points - why does that need to be a built-in feature?
I couldn't see an obvious way of doing it - since we're changing the flow in AuthorizeEndpointController to not redirect if user is not authenticated and allow_anonymous scope provided
Would we need to provide our own implementation of AuthorizeEndpointController and expose that via /.well-known/openid-configuration?
However, is this a feature you think would be useful to contribute back? I've seen other people asking for similar functionality e.g. http://stackoverflow.com/questions/31184594/access-token-for-anonymous-users-jwt. Or is this too bespoke and you would prefer to keep it away from the core product?
OK - let us discuss this internally.
Hi @dylanmorley and @leastprivilege, I'll vote for this feature, we have pretty much the same scenario mentioned by dylan.
I thought about using the acr_values to send a new flag to generate the anonymous token in the PreAuthenticateAsync of my UserService, using the regular extension points.
My concern is when the user wants to login (after being an anonymous user for a while). Since the cookie is already generated with the server, I think it will go through without asking for credentials. I could use the prompt login to force it, but then it seems the regular scenario is not as clean as it should be...
Thanks for taking the time to consider this. @dylanmorley you did a great job on the explanation, couldn't be better! I'll be eagerly waiting for Dominick's comments.
ok - give us a couple more days. (and thanks for being persistent ;)
I think the combination of handling PreAuthenticate and prompt=login is pretty OK. Would you disagree?
Well, I tried, but I most be doing something wrong because it keeps getting in the PreAuthenticate in a loop. I kept it in stand by waiting for new over here :)
I am going to take a look at it now and let you know. My guess is that since my guest user doesn't exists in my user service, methods such as IsActive are failing and/or returning false and that's creating the loop.
Let me try it and the comment on this option again.
Sure - all methods would need to special case the "anonymous" amr.
..and I would rather use an acr_value than a scope to signal the anonymous login.
OK - thanks - let me look into this
@leastprivilege so I'll make the change so that the request is made with the acr_value and the response includes the acr claim 'level 0' as per spec - http://openid.net/specs/openid-connect-core-1_0.html#IDToken.
"acr": "0" (rather than a URI \ Name)
Are you happy with acr_value as allow_anonymous?
sure - thats up to you.
@dylanmorley sorry I hijacked part of your question :(
I tried the acr_value send as part of the authentication request and handling it with the PreAuthenticate extension point and it worked just fine.
I have the special amr case on the IsActiveAsync and GetProfileDataAsync methods, basically checking if the ctx.Subject was a guest user and returning a fixed set of claims (or true in the case of IsActiveAsync).
A combination of what @dylanmorley made of the new AnonymousClaimsProvider and the regular extension points works fine.
@leastprivilege Thank you, as always you and Brock Allen are life savers :) And thank you again @dylanmorley!
what if I got a wsfed client, how can i force customer to login from there? would prompt login work?
Hi @jeremy001181, If I understand correctly your question, your wsfed will be just another provider (the same way google and facebook, or ADFS). So when you login in as a anonymous user, you don't go to any of these identity providers.
Later, when you use the prompt login, basically you are forcing the IdSrv to show the login page (where you have Google, Facebook, your wsfed server)... so I would say it will work.
Now, if you want the prompt login go to your wsfed directly, I think you are looking for the idp acr_value, you can use that together with prompt login and it should work.
I may be missing some point of your question, please elaborate if I am.
Good day!
wsfed does not have an equivalent feature to prompt=login. That's OIDC only.
@MauricioArroyo no, sorry I didn't make it clear, I use IdentityServer3.WsFederation plugin with my identityserver3 to support my wsfed clients. i understand how prompt login works, and i've got it working with oidc clients but i also have some client apps using wsfed, now i am getting a challenge how can i force anonymous users to login when they go there.
@leastprivilege thanks, i found there is a SignInValidator in wsfed plugin currently registered with Autofac using AsSelf (not interface), it doesn't have a interface otherwise i could inject my own one and force login from there?
Hi,
We've got this working using the suggestion of PreAuthenticateAsync and a number of other customisations. Because we are supporting ws-fed clients as well as Open ID, we've had to make a change to the ws-fed plug in to check for the anonymous authentication state (amr == 0) and always show login in this case. However, we're deprecating support for ws-fed over the next year, so are happier to make code changes in the plugin rather than the main repository
In our web application, we also required another authorization attribute to bounce you to the login screen if 'amr = 0' value is found in the claims. I'll share our solution when it's finalised
The only thing I'm concerned about is the redirect path when requesting anonymous tokens, which looks like this.
I would prefer an anonymous request to the authorize endpoint responds with the token directly, rather than the bounce to login and back to authorize. We're writing a javascript sdk to go with this, so obviously that has ajax implications - we're also a very high volume site with millions of unique visitors per day, so traffic paths need to be carefully considered.
We're going to performance test what we've got, but we may need to make amendments to the Authorize controller as originally spiked. Will post back what we finally go with.
hello, Can you please post the sample
Hi @Llamyae, Sorry I didn't answer before. What's is exactly what you need? I will be glad to help.
Mauricio
@MauricioArroyo actually I'm trying to give access to anonymous user to certain resources in my web app I tried to edit the PreAuthenticateAsync method to prevent the login page from displaying but no success
I see. This is what I did. I sent an acr value as part of the request, this acr values is a flag indicating that the user I am trying to login is not authenticated and I want her to be a guest. In my case I am adding a guid as part of that acr indicating what's the "username" for that guest. On the PreAuthenticateAsync if I find the acr I call an internal method that basically returns a AuthenticateResult, with a made up username, and a few claims that I need all the users to have in order to use some part of the system. What have you tried? If you give me more context I could be more helpful. :)
When I get to my computer, I can give you some snippets.
Up
On Sat, Nov 26, 2016 at 3:35 PM, MauricioArroyo notifications@github.com wrote:
When I get to my computer, I can give you some snippets.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/IdentityServer/IdentityServer3/issues/1953#issuecomment-263084236, or mute the thread https://github.com/notifications/unsubscribe-auth/AILla5Isva1kZxviWEWEBJQqN9tU16ETks5rCJghgaJpZM4GDrH7 .
Hey, sorry I forgot... too busy on the weekend... up means you figure it out?
no I'm waiting for your response .
On Mon, Nov 28, 2016 at 3:25 PM, MauricioArroyo notifications@github.com wrote:
Hey, sorry I forgot... too busy on the weekend... up means you figure it out?
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/IdentityServer/IdentityServer3/issues/1953#issuecomment-263384040, or mute the thread https://github.com/notifications/unsubscribe-auth/AILla2oiFq4AozVvHzeOVIK4evGiE-jVks5rCzjTgaJpZM4GDrH7 .
Oh, sorry. Ok here it goes. Where I am in a place that needs the user to be authenticated and falls back to the guest user. Basically I do two things (if the user is not already authenticated of course):
On the Startup, under OpenIdConnectAuthenticationNotifications, I catch if the flag was set, on the RedirectToIdentityProvider. if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.AuthenticationRequest){ if the flag is set, I add the following to the message ProtocolMessage.AcrValues = $"{flagId}:{guestId}"; }
After than on the IdentityServer PreAuthenticateAsync, I check if the AcrValue exists, If it does, I do the guest authentication, that's straight forward. public override async Task PreAuthenticateAsync(PreAuthenticationContext context) { if the Acr for guest is part of context.SignInMessage.AcrValues return AuthenticateResult with the values for your guest use. In our case, I use the guestId to generate a fake username }
I hope this helps. Just fill in the blanks with your own ids and you are set to go!
Thank you, I'll try this and let you know.
On Mon, Nov 28, 2016 at 3:43 PM, MauricioArroyo notifications@github.com wrote:
Oh, sorry. Ok here it goes. Where I am in a place that needs the user to be authenticated and falls back to the guest user. Basically I do two things (if the user is not already authenticated of course):
- Add to the OwinContext a flag indicating I would need a guest user authentication. In my case, I send the Guid that I use for the guest cookie. Request.GetOwinContext().Set("NAME OF YOUR CHOOSING", guestId.ToString());
- Return from the action (I am using MVC) a Challenge result return new ChallengeResult("OpenIdConnect", url);
On the Startup, under OpenIdConnectAuthenticationNotifications, I catch if the flag was set, on the RedirectToIdentityProvider. if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType. AuthenticationRequest){ if the flag is set, I add the following to the message ProtocolMessage.AcrValues = $"{flagId}:{guestId}"; }
After than on the IdentityServer PreAuthenticateAsync, I check if the AcrValue exists, If it does, I do the guest authentication, that's straight forward. public override async Task PreAuthenticateAsync(PreAuthenticationContext context) { if the Acr for guest is part of context.SignInMessage.AcrValues return AuthenticateResult with the values for your guest use. In our case, I use the guestId to generate a fake username }
I hope this helps. Just fill in the blanks with your own ids and you are set to go!
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/IdentityServer/IdentityServer3/issues/1953#issuecomment-263388331, or mute the thread https://github.com/notifications/unsubscribe-auth/AILla7xVI23mlMcR6xJGgCrP6KSL5wOtks5rCz0BgaJpZM4GDrH7 .
sorry for bothering you again,I have few questions,
do you generate a random new Guid everytime ? and where in your code you place this method,if it's possible can you share the code
On Mon, Nov 28, 2016 at 3:50 PM, wrote:
Thank you, I'll try this and let you know.
On Mon, Nov 28, 2016 at 3:43 PM, MauricioArroyo notifications@github.com wrote:
Oh, sorry. Ok here it goes. Where I am in a place that needs the user to be authenticated and falls back to the guest user. Basically I do two things (if the user is not already authenticated of course):
- Add to the OwinContext a flag indicating I would need a guest user authentication. In my case, I send the Guid that I use for the guest cookie. Request.GetOwinContext().Set("NAME OF YOUR CHOOSING", guestId.ToString());
- Return from the action (I am using MVC) a Challenge result return new ChallengeResult("OpenIdConnect", url);
On the Startup, under OpenIdConnectAuthenticationNotifications, I catch if the flag was set, on the RedirectToIdentityProvider. if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.Authe nticationRequest){ if the flag is set, I add the following to the message ProtocolMessage.AcrValues = $"{flagId}:{guestId}"; }
After than on the IdentityServer PreAuthenticateAsync, I check if the AcrValue exists, If it does, I do the guest authentication, that's straight forward. public override async Task PreAuthenticateAsync(PreAuthenticationContext context) { if the Acr for guest is part of context.SignInMessage.AcrValues return AuthenticateResult with the values for your guest use. In our case, I use the guestId to generate a fake username }
I hope this helps. Just fill in the blanks with your own ids and you are set to go!
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/IdentityServer/IdentityServer3/issues/1953#issuecomment-263388331, or mute the thread https://github.com/notifications/unsubscribe-auth/AILla7xVI23mlMcR6xJGgCrP6KSL5wOtks5rCz0BgaJpZM4GDrH7 .
The guid gets saved in a "guest" cookie, so it doesn't change for that user in that particular machine. So every time I need to log in as guest, I use the cookie to extract the Guid. The place where you create the cookie will be up to you, but I would recommend when you need it to send the request on the Identity Server, create it if it doesn't exists, and it does exist, just take out the guid you need.
did it work?
no it didn't . I have problem editting this line doesn't recognize the guestID , itProtocolMessage.AcrValues = $"{flagId}:{guestId}"
I am not quite getting what you mean. Could you please post the snippet. GuestId is a variable.
I created a guest cookie like you suggestes, and in the startup file I added this method ` RedirectToIdentityProvider = n => {
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.AuthenticationRequest)
{
n.ProtocolMessage.AcrValues = $"{flagId}:{GuestID}";
}`
What do flagID and GuestID variables have? You need to take the GuestID out of the OwinContext.
like this var id = n.OwinContext.Get<int>(GuestID);
You have to make sure you included that in the context. If your id is an int, then I guess it's ok, but if it's a Guid, then it's not. Then if you have the variable id, you need to change the {GuestID} for {id} in the string interpolation.
it's a GUID , so how to do that
Not sure what you mean. You can't get a guid as int, you know that right? save the guid in the context as string and then retrieve it as string too... if I am understanding your question correctly.
I'm not sure I get it right
var GuestID = n.OwinContext.Get
wen I run the app the GuestID is always null !
Sorry I didn't answer before. I was out of town. Are you setting the value in the OwinContext before with the same key?
yes I did it works, but the login page keeps displaying it's like the PreAuthenticateAsync never get executed even though I specefied it in the startup file like this factory.UserService = new Registration<IUserService, TestUserService>();
hmm, the PreAuthenticateAsync method not being executing is weird, it should if you challenge the authentication even without the flags, so that's not an issue of this change, you will need to check the configuration of the site... is it configured to use the IdSvr?
yes it is
Create the smallest project you can with your configuration and post it so I can take a look. Would it be possible?
ok i will thank you
We're working on an ecommerce site that is moving towards a micro service architecture, we need to integrate with 'shopping bag' and 'saved items' services, both of which can be accessed in either an authenticated or unauthenticated state. For example, the customer may save some items, switch them all to bag, then checkout as a guest.
The problem
We want to apply bearer token validation to our APIs so that we have a standard, consistent authentication mechanism - but how should this work when the customer has not signed in? In the above scenario, we're never prompting the user for credentials.
We don't want to maintain multiple endpoints and have logic about which one to call, we want a single endpoint, secured by bearer token, and the client calls that regardless of their authentication state. So, how do they identify themselves uniquely so they can present a token to an API?
Acquire an anonymous token
We're writing a javascript SDK that we'll drop on relying party pages - it will make use of the Identity Server oidc to determine if the user is authenticated by polling the STS in the normal manner. If they aren't, then we'll make a request for a token and pass in a new scope anon
This would be passed through into DefaultTokenService which would determine that it should issue an anonymous token. We're proposing that this will have a standard set of claims and look something like this
amr = anon did = device ID, a GUID generated and also applied to the subject
This can then be used to call APIs that are secured by bearer token, and logic within those APIs can check for the amr claim and take appropriate action.
I hacked in a changeset last night just for proof of concept, and will be working on this today to refine - these are the touchpoints I can see.
https://github.com/dylanmorley/IdentityServer3/commit/de073d7b10a542338c211bfe8f6e6404931afb59
@brockallen @leastprivilege - Any thoughts on this - do you agree it's a valid use case? Any security concerns you can see?
We researched AWS cognito when attempting to spec this out - http://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements.htm and have followed the same pattern regarding amr claim and GUID for identification.
Would you consider bringing 'anon' functionality into the main branch if you're happy with the work we do, or should we branch away?
Thanks