Closed SetTrend closed 8 years ago
Will there be documentation on the server? Flow diagrams? Extension interface description?
Yes, eventually. Writing documentation is an extremely time-consuming task (that's why there's no documentation yet).
The ASP.NET 4 AuthorizationServer implementation was quite quirky, having loads of side effects hidden in the implementation and strange method naming.
Not sure what "side effects" you're referring to (I would love to hear more about that), but one thing is sure: if you didn't like OAuthAuthorizationServerMiddleware
, you won't like this project, since it leverages the same low-level/protocol-first approach.
I sure comprehend.
Still it helps cleaning the architecture when trying to explain why things are supposed to be used in a particular way.
After experimenting with ASP.NET Identity/AuthenticationServer for the last couple of days. I'm still not sure whether I grasped AuthenticationServer correctly. Perhaps some of my notions are wrong so please forgive me if one or the other of the following statements will probably not withstand being put to the proof.
Yet, I mainly feel that AuthenticationServer fails in mixing implementation with interface. Using it seems no true relief compared to going the way yourself.
GetExternalLogin()
) is required to be the same as the one that's being called at the end of the authentication handshake for it to be recognized as valid redirect_url, what's the reason in specifying it in AuthorizeEndpointPath
, too? The initial call provides all the information.TokenEndpoint(OAuthTokenEndpointContext context)
)? That's another of the many implementation details revealed unnecessarily. (From my point of view.)I would have enjoyed an abstraction that would keep me from dealing with OAuth2 in general. I would have enjoyed an implementation without a requirement for things like GetExternalLogin()
, ISecureDataFormat<AuthenticationTicket>
or other implementation details I'm not interested in when creating a website or web service. ClaimsIdentity
and User
should be the only objects in regard to authentication I should be interested in - unless I want to do something special, of course.
But the main path should be just one single Katana call for logging in, providing it with an authentication provider name, and that should be the end of the story for me as a programmer until a valid ClaimsIdentity
is getting returned. No HostAuthenticationAttributes
, AuthenticationTypes
or similar. That'd all be Katana's cup of tea. ... For an "internal" provider I would have to provide the corresponding authentication back-end, of course.
Yet, I mainly feel that AuthenticationServer fails in mixing implementation with interface. Using it seems no true relief compared to going the way yourself.
Actually, OAuthAuthorizationServerMiddleware
should be considered more as an OAuth2 server framework than as a ready-to-use/turnkey server. One thing to remember is that, by default, it operates in a pass-through mode (at least for the authorization endpoint): the middleware validates the request and you're expected to implement the consent part using the middleware of your choice (Nancy or MVC for instance). This clearly differs from other servers, where this part is usually not left as an exercise. On the other hand, it offers the most flexible approach and allows you to do virtually anything you want to. It definitely has its pros/cons.
The AuthenticationServer interface doesn't distinguish clear enough between implementation and abstraction. All these "context" parameters reveal too much implementation detail and don't focus on the least necessary requirements.
I'm tempted to say that's inherent to the protocol-first/low-level design of the OAuthAuthorizationServerMiddleware
. TBH, I don't think the built-in contexts expose too much information: advanced users must be able to retrieve all the parameters of an OAuth2 request if they need to. ASOS (this project) goes even beyond and makes the OpenIdConnectMessage
primitive the first-class citizen.
Validated() - what's this term supposed to mean? Past perfect? Subjunctive mood? If it already happened (past perfect), then why should I call it? - What does it do?
That's an interesting remark: I'll ping a few ASOS users and ask them whether Validate()
/Skip()
/Reject()
might be clearer names than Validated()
/Skipped()
/Rejected()
.
In a nutshell, these methods are used to inform OAuthAuthorizationServerMiddleware
whether a request should be validated or rejected: this pattern is used in a few events like ValidateClientRedirectUri
or ValidateClientAuthentication
, where you're respectively expected to validate the redirect_uri
used in the authorization request and the client credentials used in a token request.
If the action supposed to initiate an external authentication handshake (GetExternalLogin()) is required to be the same as the one that's being called at the end of the authentication handshake for it to be recognized as valid redirect_url, what's the reason in specifying it in AuthorizeEndpointPath, too? The initial call provides all the information.
I just took a look at the default Web API 2 template that comes with VS 2015 and I agree it's extremely weird: it's inspired from the SPA template except there's no JS client part. Not sure who wrote this thing, but it clearly sucks. IMHO, the SPA template looks a bit better.
Anyway, GetExternalLogin
does actually 3 different things that you must not mix up to clearly understand how it works:
OAuthAuthorizationServerMiddleware
after the user has been authenticated by the external social provider. It's the Authentication.SignIn(properties, oAuthIdentity, cookieIdentity);
line that asks the OAuth2 server to return the user to the client application, by issuing a 302 response with the access_token in the URL.Ideally, I agree that everything should be decoupled.
If all these provider specifics are abstracted, why doesn't a similar provider for internal authentication exist? Why isn't there an option to access the internal authentication just like all the external user logins?
What do you mean exactly by "internal authentication"?
Too much implementation detail: Why should I be interested in validating client ids manually? Why doesn't the base implementation deal with the default case gracefully?
Because there's actually no default case in OAuth2 :smile:
The OAuth2 authorization server has absolutely no idea how you'll use it (do you plan to support third-party apps? Or just your own SPA facade?). Of course, it's not specific to this server: all servers will need you to pre-register the client applications somewhere (in hard code, in a database...) and refuse to serve OAuth2 requests containing an unknown/unvalidated client_id
.
Why should I be interested in copying properties manually (TokenEndpoint(OAuthTokenEndpointContext context))? That's another of the many implementation details revealed unnecessarily. (From my point of view.)
I agree, it's a huge fail. But again, it's not caused by the OAuth2 authorization server but by the boilerplate code added by the templates writers :smile:
In your own code, you're - of course - strongly discouraged to exposed all your authentication properties like the default ApplicationOAuthProvider
does!
I would have enjoyed an abstraction that would keep me from dealing with OAuth2 in general. I would have enjoyed an implementation without a requirement for things like GetExternalLogin(), ISecureDataFormat
or other implementation details I'm not interested in when creating a website or web service. ClaimsIdentity and User should be the only objects in regard to authentication I should be interested in - unless I want to do something special, of course.
TBH, I have absolutely no idea why the Web API 2 VS 2015 template uses that. This horrible ExternalAccessToken
shouldn't even exist, as you don't need that to retrieve the identity associated with an external social provider (the ExternalCookie
is specially made for that).
Excellent explanation!
Thank your very much for enlightening me. This really sheds some light to me on where to semantically cut things apart.
Though, I'm still do have some questions.
(I'm very sorry for this being a very long post. But for explaning the details of my concerns I need to become verbose. Please accept my apologies.)
GetExternalLogin()
:It redirects you to the external authentication provider you select (e.g Facebook or Google). It's indeed an OAuth2 flow, but it has nothing to do with the OAuth2 authorization server. Basically, the Web API template just behaves like any OAuth2 client.
I sure apprehend it's just "some" Web API action that's being called and calls itself some AuthenticationServer functionality. It basically just initiates the AuthenticationServer workflow. But if it's not part of AuthenticationServer, then why is it part of AuthenticationServer's Options object?
Let me explain why I'm asking this:
In an experiment I copied the whole GetExternalLogin()
function, renamed it to GetExternalLogin1()
and called my copy (GetExternalLogin1()) by the client. Everything else remained: I kept the original function and the original function was still mentioned in OAuthAuthorizationServerOptions
: AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
. This is what happened:
GetExternalLogin1()
was called by the client.GetExternalLogin1()
was called again, so the original URL was stored somewhere.So, if the initial call to GetExternalLogin1()
apparently gets stored somewhere inside AuthenticationServer, what's the use of the additional AuthorizeEndpointPath
Options property? It doesn't seem to provide any benefit.
internal authentication
:Right. My mistake. Everything is in place already. I got lost in the topic in the course of evaluation.
default client id
:Good call. My mistake again. Please see above.
too much implementation detail
:I would propose to separate advanced scenarios from basic scenarios. If advanced programming was required then advanced programmers would know where to look for these properties. But basic programmers woudn't be bothered with loads of properties not pertaining to a particular IOAuthAuthorizationServerProvider
method.
BaseContext
never ever changes in the course of an OAuth2 workflow. That's why its a "context". So I propose to remove the context part from all the IOAuthAuthorizationServerProvider
methods' parameters and have it become a protected member of the OAuthAuthorizationServerProvider
. It's an OAuthAuthorizationServerProvider
implementation detail and there's no place for it in an interface anyway.
That would change OAuthAuthorizationServerProvider
to:
public class OAuthAuthorizationServerProvider
{
protected BaseContext WorkflowContext; // no need for generics
...
}
... and remove all these "contexts" from the interface's methods:
public interface IOAuthAuthorizationServerProvider
{
...
Task ValidateClientRedirectUri(ClientRedirectUri args);
Task ValidateTokenRequest(TokenRequest args);
}
Then we could get rid of all these calamitous "context" names everywhere (see my example above). With all these different "context" items buzzing everywhere around it's hard to keep track of which "context" one's dealing with in a particular situation.
To me it feels a design flaw to have all these contexts buzz around. If there would have been so many contexts in the early days we probably wouldn't have int
types today but intContext
types :smirk:
An advanced example implementation would then look like:
public override Task ValidateClientRedirectUri(ClientRedirectUri args)
{
if (WorkflowContext.OwinContext.Request.Method.Equals("get"
, StringComparison.OrdinalIgnoreCase)) args.Validated();
}
Moreover, I feel the BaseContext
class has two redundant properties, adding even more unnecessary complexity:
public abstract class BaseContext<TOptions>
{
protected BaseContext(IOwinContext context, TOptions options);
public TOptions Options { get; }
public IOwinContext OwinContext { get; }
public IOwinRequest Request { get; } // redundant
public IOwinResponse Response { get; } // redundant
}
The OwinContext
property comes with Request/Response already. So there really is no need in providing these proprties in derived classes again and again and again ...
Moreover, there's not need for BaseContext
being a generic. The OAuthAuthorizationServerProvider
class's code is not able to deal with anything else but the original OAuthAuthorizationServerOptions
class. So there is no need to introduce the complexities of a generic here.
If any derived provider class would want its own options then it's free to use its own Options member field or fields. There is absolutely no need to propagate private options to any down below infrastructure as it can't access them anyway.
In addition, using a function parameter callback as the return value of a validation function seems rather odd to me. For instance, why is ValidateClientAuthentication()
returning void
when it actually is supposed to return a validation result?
I would suggest to refurbish the IOAuthAuthorizationServerProvider
interface to something similar to this:
public enum ValidationResult
{
Validated
, Rejected
}
public interface IOAuthAuthorizationServerProvider
{
Task<ValidationResult> ValidateAuthorizeRequest(AuthorizeRequest args);
Task<ValidationResult> ValidateClientAuthentication(ClientAuthentication args);
Task<ValidationResult> ValidateClientRedirectUri(ClientRedirectUri args);
Task<ValidationResult> ValidateTokenRequest(TokenRequest args);
Task<AuthenticationTicket> GrantResourceOwnerCredentials(GrantResourceOwnerCredentials args);
}
I sure apprehend it's just "some" Web API action that's being called and calls itself some AuthenticationServer functionality. It basically just initiates the AuthenticationServer workflow. But if it's not part of AuthenticationServer, then why is it part of AuthenticationServer's Options object?
Because the authorization endpoint is an OAuth2 concern: though the OAuth2 server middleware doesn't render any consent view by default, it extracts the OAuth2 request and validates it to ensure the client_id
/redirect_uri
couple is not bogus. It's even more obvious in ASOS, where the server middleware has to decide whether the authentication request should be extracted from the query string or the form body.
So, if the initial call to GetExternalLogin1() apparently gets stored somewhere inside AuthenticationServer, what's the use of the additional AuthorizeEndpointPath Options property? It doesn't seem to provide any benefit.
This property is essential since it's used to restrict the paths the OAuth2 server should extract and validate the authorization request from. In ASOS, this property is also extremely important, since it's exposed via the OIDC metadata endpoint.
BaseContext never ever changes in the course of an OAuth2 workflow. That's why its a "context". So I propose to remove the context part from all the IOAuthAuthorizationServerProvider methods' parameters and have it become a protected member of the OAuthAuthorizationServerProvider. It's an OAuthAuthorizationServerProvider implementation detail and there's no place for it in an interface anyway.
Then we could get rid of all these calamitous "context" names everywhere (see my example above). With all these different "context" items buzzing everywhere around it's hard to keep track of which "context" one's dealing with in a particular situation.
Sadly, this wouldn't work: the authorization provider is necessarily a singleton, so you can't have shared fields/properties.
Having specific context class has a huge advantage: we can add new properties without having to modify the provider signatures.
The OwinContext property comes with Request/Response already. So there really is no need in providing these proprties in derived classes again and again and again ...
These properties are just convenient ways to access the OWIN request/response. In ASOS, I "replaced" them by equivalent properties returning OpenIdConnectMessage
instead of HttpRequest
/HttpResponse
.
Moreover, there's not need for BaseContext being a generic. The OAuthAuthorizationServerProvider class's code is not able to deal with anything else but the original OAuthAuthorizationServerOptions class. So there is no need to introduce the complexities of a generic here.
This class is generic because it's shared by all the security middleware, that all have different options classes (e.g GoogleAuthenticationOptions
, TwitterAuthenticationOptions
, OAuthAuthorizationServerOptions
). Having a generic parameter was just a convenient way to avoid creating new Options
properties in subclasses (and was not really harmful, since you never had to deal directly with the generic type). Note that this generic parameter was removed in ASP.NET 5.
In addition, using a function parameter callback as the return value of a validation function seems rather odd to me. For instance, why is ValidateClientAuthentication() returning void when it actually is supposed to return a validation result?
Your enum would be too limited, since we allow the developer to set his/her own error code/description. Same remark for AuthenticationTicket
, which wouldn't allow you to return an error.
This property is essential since it's used to restrict the paths the OAuth2 server should extract
So, if I understand you right, this property accepts a pattern?
Sadly, this wouldn't work: the authorization provider is necessarily a singleton, so you can't have shared fields/properties.
In that case, I suppose all the singleton properties to become static. Then the non-singleton properties could be set in the constructor. That wouldn't cost much, it'd be just some pointer assignments. And it would help thinning out the final method arguments.
Your enum would be too limited, since we allow the developer to set his/her own error code/description.
From the samples I have seen so far (e.g. BitOfTech) no other action has been taken in common scenarios. However, returning such intrinsic value wouldn't limit the programmer from implementing additional steps within the validation method. The only thing that would change would be that the interface would state in a clear manner what it is expecting from the programmer.
Same remark for AuthenticationTicket, which wouldn't allow you to return an error.
This is a design decision. It depends on whether the occurance of an error is more or less the common case in an application or whether the occurance of an error is rather exceptional. In that case a more appropriate approach would be to throw a corresponding exception instead of returning an error value. In that case the run-time overhead would easily be compensated by a cleaner interface, lacking the use of too much if
s (if := Italien Food := Spaghetti Code).
So, if I understand you right, this property accepts a pattern?
Nope (the plural form wasn't appropriate :smile:), but you can dynamically determine the endpoint type from the MatchEndpoint
event and thus "extend" the paths associated with the authorization endpoint. You can see https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Server/blob/vNext/samples/Mvc/Mvc.Server/Providers/AuthorizationProvider.cs#L96-L102 for an example.
In that case, I suppose all the singleton properties to become static. Then the non-singleton properties could be set in the constructor. That wouldn't cost much, it'd be just some pointer assignments. And it would help thinning out the final method arguments.
When I said the provider was necessarily a singleton, I meant the provider registered in the middleware options was shared between the requests. But actually, you can have multiple OIDC servers in the same app and thus, different instances (one per server).
From the samples I have seen so far (e.g. BitOfTech) no other action has been taken in common scenarios. However, returning such intrinsic value wouldn't limit the programmer from implementing additional steps within the validation method. The only thing that would change would be that the interface would state in a clear manner what it is expecting from the programmer.
Because they are just blog samples, meant to be simple to read and understand :smile: For a real world example, you can take a look at OpenIddict, the whole new OIDC server I'm currently working on (it uses ASOS internally, but offers a much easier experience): https://github.com/openiddict/core/blob/dev/src/OpenIddict.Core/OpenIddictProvider.cs#L81 https://github.com/openiddict/core/blob/dev/src/OpenIddict.Core/OpenIddictProvider.cs#L150
This is a design decision. It depends on whether the occurance of an error is more or less the common case in an application or whether the occurance of an error is rather exceptional. In that case a more appropriate approach would be to throw a corresponding exception instead of returning an error value. In that case the run-time overhead would easily be compensated by a cleaner interface, lacking the use of too much ifs (if := Italien Food := Spaghetti Code).
OAuth2 errors are definitely not "exceptional", specially in the validation events :smile:
Thank you so much for enlightening me on this!
I've been on vacation last week so I couldn't reply yet ...
From the code sample you provided (https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Server/blob/vNext/samples/Mvc/Mvc.Server/Providers/AuthorizationProvider.cs#L96-L102), does this imply that if one would set AuthorizeEndpointPath = new PathString("/"),
any local URL would work properly?
Hope you enjoyed your holidays! :clap:
From the code sample you provided (https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Server/blob/vNext/samples/Mvc/Mvc.Server/Providers/AuthorizationProvider.cs#L96-L102), does this imply that if one would set AuthorizeEndpointPath = new PathString("/"), any local URL would work properly?
Yep :smile:
I see ... But, hmm, that's different then from the "original" Katana authorization server implementation, isn't it?
I just tested it in the original Web API scaffolding code and it didn't work with Katana. With Katana it looks like the AuthorizeEndpointPath
property must always match the action that is calling Request.GetOwinContext().Authentication.Challenge(LoginProvider);
. If my observation was true the AuthorizeEndpointPath
would be redundant as it always must exactly match the Challenge()
member function's caller action.
Does ASOS behave differently here?
I see ... But, hmm, that's different then from the "original" Katana authorization server implementation, isn't it?
Nah, that's exactly the same implementation: by default, the request path must exactly match the registered authorization endpoint path, but you can relax that using the MatchEndpoint
notification.
I just tested it in the original Web API scaffolding code and it didn't work with Katana. With Katana it looks like the AuthorizeEndpointPath property must always match the action that is calling Request.GetOwinContext().Authentication.Challenge(LoginProvider);. If my observation was true the AuthorizeEndpointPath would be redundant as it always must exactly match the Challenge() member function's caller action.
No, you can call Challenge
everywhere and this code path has nothing to do with the authorization server: it's just used to redirect your users to the external provider.
Given all that there still is a blurry part that's truely confusing me (I don't want to bother you overtime. Please only answer if you enjoy to answer):
I took the original Win API scaffolding code, which worked flawlessly ...
Then I copied the GetExternalLogin() member function code and used that copy for initiating the authentication process. Everything else I left unchanged. Given from what you've written everything should run flawlessly, because the OAuthAuthorizationServerOptions.AuthorizeEndpointPath
property still points to the original action – yet, authentication fails to complete.
[OverrideAuthentication]
[HostAuthentication(DefaultAuthenticationTypes.ExternalCookie)]
[AllowAnonymous]
[Route("LoginExternal", Name = "LoginExternal")]
public async Task<IHttpActionResult> GetLoginExternal(string provider, string error = null)
{
return await GetExternalLogin(provider, error);
}
[AllowAnonymous]
[Route("ExternalLogins")]
public IEnumerable<ExternalLoginViewModel> GetExternalLogins(string returnUrl, bool generateState = false)
{
...
foreach (AuthenticationDescription description in descriptions)
{
ExternalLoginViewModel login = new ExternalLoginViewModel
{
Name = description.Caption,
Url = Url.Route("LoginExternal", new
...
State = state
};
logins.Add(login);
}
return logins;
}
public GetLoginExternal()
{
return GetExternalLogin();
}
public GetExternalLogins()
{
...
Url = Url.Route("LoginExternal",
...
}
... while keeping the original AuthorizeEndpointPath
setting:
OAuthOptions = new OAuthAuthorizationServerOptions
{
...
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
...
};
The result is that ValidateClientRedirectUri()
isn't called and redirect_url
isn't followed.
So my conclusion is that the value of AuthorizeEndpointPath
doesn't have any influence on the authentication process. It must mandatory match the initial action call. So the initial action call itself seems to fully and exclusively determine the endpoint path.
Without seeing the full source, it's hard to help you. You should create a new GitHub repository and upload your solution.
So my conclusion is that the value of AuthorizeEndpointPath doesn't have any influence on the authentication process.
True. As already mentioned, the authorization and authentication steps are totally distinct :smile:
I have now created a repository with the project here: https://github.com/SetTrend/WebApiAuthenticationTest
It's the plain default Web API 2.2 template for individual user authentication with the following amendmends:
AccountController.cs
: Changes mentioned above.Startup.Auth.cs
: Google Client/Secret information - must be added here.Test.html
: Initial project page, used to query external providers and to show login buttonsAfter downloading the project please set "html/Test.html" as the project start page, add your personal Google client/secret and run the project.
You will notice that the project runs flawlessly when using const string action = "ExternalLogin"
while it won't redirect to the root page when using const string action = "LoginExternal"
in GetExternalLogins()
.
This is purely about authentication. No authorization has taken place up to that point. None of the actions has been decorated with the AuthorizeAttribute
.
FYI, the PR renaming Validated
/Rejected
/Skipped
to Validate
/Reject
/Skip
as you suggested has been merged: https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Server/pull/180.
I'll take a look at your demo.
I was thinking you were to evaluate the project I created for you on Github to reproduce the problem AuthorizationServer has?
const string action = "LoginExternal"
doesn't work because it doesn't match the authorization endpoint defined in your Startup class: the authorization server doesn't extract the authorization request and your subsequent SignIn
call is simply ignored.
Replace AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
by AuthorizeEndpointPath = new PathString("/api/Account/LoginExternal"),
and it will work.
Well, that's what I meant: As the AuthorizationEndpointPath
property must match the URL, it's plain redundant. There is either no reason to restrict the URL or the action initiating the challenge can temporarily store its own URL somewhere in the OWIN context. I believe there is no need to store an existing information (at runtime) twice.
There is either no reason to restrict the URL or the action initiating the challenge can temporarily store its own URL somewhere in the OWIN context.
There's always a reason, you just ignore it :smile: I can give you at least 2 justifications:
ApplicationCanDisplayErrors
is set to true. Not constraining the authorization endpoint would reject every other MVC/Web API request (as the authorization server would consider them as invalid OAuth2 request).Hmm, then why don't I get this error page when initiating the OAuth2 sequence with the LoginExternal
endpoint? Everything works flawlessly - except for that I cannot sign in.
You will notice that the AuthorizationEndpointPath
property is replaced by a temporarily stored value.
I don't imagine ASOS deals with requests other than those initially returning a 401?
Hmm, then why don't I get this error page when initiating the OAuth2 sequence with the LoginExternal endpoint? Everything works flawlessly - except for that I cannot sign in.
Easy: since your LoginExternal
endpoint doesn't correspond to the authorization endpoint path, the authorization server ignores the request and that's why you don't see the error page.
You will notice that the AuthorizationEndpointPath property is replaced by a temporarily stored value.
And at "step 3", how do you determine whether the request is an OAuth2 request? Where do you store this "URL" thingy? How do you make the provider configuration endpoint work, since you have no initial authentication roundtrip?
I don't imagine ASOS deals with requests other than those initially returning a 401?
There's absolutely nothing in ASOS (or in OAuthAuthorizationServerMiddleware
) that checks whether the response will return 401 or not.
Actually, even with the "misconfiguration" set the whole workflow occurs: The redirection to, say, Google, and the call to the "correctly" configured endpoint occur after the external authentication roundtrip.
Remember, both endpoints exist in the sample I provided. I'm just calling the not-configured endpoint at the beginning in step #1. At the end of the external provider sequence the configured endpoint is called, I guess due to the property value.
So, if not ASOS, what is creating the redirection in step #3 when the original result actually was a 401? (I comprehend the 401 is required to initialize the OWIN sequence because bearer authentication is set to Passive.) The step that's creating the initial 302 response, isn't that ASOS? What's triggering it? Whatever it is, it's definitively not related to AuthorizationEndpointPath
property.
So, if not ASOS, what is creating the redirection in step #3 when the original result actually was a 401? (I comprehend the 401 is required to initialize the OWIN sequence because bearer authentication is set to Passive.) The step that's creating the initial 302 response, isn't that ASOS?
Nope, it's not ASOS (nor OAuthAuthorizationServerMiddleware
), it's your account controller: https://github.com/SetTrend/WebApiAuthenticationTest/blob/master/AuthenticationWebApiTest/Controllers/AccountController.cs#L248
Nope, the account controller is creating a 401 response. But it's not creating a 302 response. That must be ASOS.
LOL, I know OAuthAuthorizationServerMiddleware
and ASOS enough - obviously - to guarantee you it doesn't redirect you to something else than the client applications.
The 302 response you're seeing is applied by the social provider you're selecting (e.g Google), not by ASOS.
Absolutely, yes :hand:
And I must admit that due to lack of documentation I don't know the entrails of any of those :blush:
OK, if that's the case, I wonder what's the particular part of ASOS here? When does it come into play in this sequence?
And I must admit that due to lack of documentation I don't know the entrails of any of those :blush:
Almost everything related to authentication is considered as highly advanced stuff and has almost no documentation. But here are the relevant parts that handle the 401 -> 302 transformation:
ApplyResponseChallengeAsync
: https://github.com/jchannon/katanaproject/blob/master/src/Microsoft.Owin.Security.Google/GoogleOAuth2AuthenticationHandler.cs#L149 (it works exactly the same way with the other providers).ApplyRedirect
: `https://github.com/jchannon/katanaproject/blob/master/src/Microsoft.Owin.Security.Google/Provider/GoogleOAuth2AuthenticationProvider.cs#L23OK, if that's the case, I wonder what's the particular part of ASOS here? When does it come into play in this sequence?
ASOS only extracts the authorization request (if the request path corresponds to the authorization endpoint) and catches the SignIn
call to create the tokens and redirect the user agent to the client application. Nothing less, nothing more.
OK, I comprehend now.
Since MVC6 RC1 has been released and deployed now, do you perhaps know where to get an ASOS sample implementation for this kind of project setup:
(The above depicts a setup comprising of an ASOS SSO server, incl. external authentication, plus n Web API client projects.)
I noticed that the Katana team doesn't respond to questions regarding Katana anymore because they regard the project as obsolete (although it still is the latest release version available). They only respond to MVC6 questions (although it hasn't been released yet and, thus, can't be used for production. What a ludicrous world.).
There's currently no real sample using a JS client, but it's planned (it will be added to https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Samples). That said, the server part shouldn't really change when using JS clients.
You can take a look at these prototypes:
For all that you've been writing to me by now, I'd like to thank you. ... I don't know how to put this into the right words... Please forgive me if I sound ridiculous now ... You have been a invaluable help, and you've been shedding an utterly amount of patience with me. My writing often sounds demanding, provoking and polemical. But that's the only way I can write and learn at the same time. You've been ignoring this and happily answered all my (partially stupid) questions.
Thank you, Kévin.
Merry Christmas, Axel
Hey @SetTrend. I'm planning to add structure quite similar to what you want here: https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Samples/issues/3#issue-119113685
It's gonna have one project with SignalR SPA frontend, server and another one, with WebAPI in another project, or sth similar.
If you wanted to contribute TypeScript / Angular frontend project, that'd be great! Also, me and @PinpointTownes could help with setting up WebAPI project.
For all that you've been writing to me by now, I'd like to thank you. ... I don't know how to put this into the right words... Please forgive me if I sound ridiculous now ... You have been a invaluable help, and you've been shedding an utterly amount of patience with me.
No problem :smile: Thank you very much for these kind words! :clap:
My writing often sounds demanding, provoking and polemical. But that's the only way I can write and learn at the same time. You've been ignoring this and happily answered all my (partially stupid) questions.
There's no stupid question (specially concerning a barely documented library) :smile:
And don't worry, your remarks/questions were not polemical (else, I'd have locked this ticket :trollface:). Don't hesitate to keep posting your questions if you still have ones :clap:
Merry Christmas,
Merry Christmas, Axel! :gift:
@DovydasNavickas :
That would be absolutely fabulous! Yes, please, count me in.
I highly appreciate your offer to help me in creating the Web API part!
Which kind of requirements would you want to see fullfilled by the front-end part? Are there specific framework versions you would request to be used (Angular v1/2, Typescript v1.x)?
Do you believe we have a chance to get this finished til January? In January I would want to get back to work. That would then considerably restrict the time I could spend in this project.
Let's go to the the issue I mentioned (https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Samples/issues/3#issue-119113685) and talk there. More structure is always better :clap:
Will there be documentation on the server? Flow diagrams? Extension interface description?
The ASP.NET 4 AuthorizationServer implementation was quite quirky, having loads of side effects hidden in the implementation and strange method naming.
I wouldn't want to dig through code again just to research how OpenIdConnect.Server behaves.