What is the right way to start Authorization Code Flow from identity server login page? - reactjs

I'm trying to implement Authorization Code Flow for SPA React client with ASP.NET Core and IdentityServer4.
There are two scenarios:
1) User open SPA app, we check if he has an access token and if he hasn't we generate url like
/connect/authorize?
client_id=*client_id*&
redirect_uri=*redirect_uri*&
response_type=code&
response_mode=fragment&
state=*some_state*&
nonce=*some_nonce*&
code_challenge=*code_challenge*&
code_challenge_method=S256&
scope=openid profile email
And so Authorization Code Flow starts. This works pretty clear and after all round trips user comes back to SPA app with code then send request for token (include code and code_verifier) then receive it and with happiness in soul continue using our great application.
2) User opens login page directly and here is where I'm stuck. IdentityServer context knows nothing about this user, code challenges etc. because we didn't make request to /connect/authorize before going to this page as in normal flow. What's next?
I can generate /connect/authorize link directly in login page and do ugly redirect to it and then back to login page (what I don't want to do honestly), but how my SPA app will know what code_verifier I generate here? Of course I can store it in some shared cross-domain cookie, but here should be something better approach I believe.
Another solution I can redirect user from login page to my app, it recognizes that user not authorized and we start scenario #1. Also not my go to approach I think.
What should I do in case user opens my identity server page directly?
Is this possible using Authorization Code Flow or should I consider combine some other flows with this one?
I don't want to use Implicit Flow due to new recommendation from OAuth 2.0 specification.

Quite a simple answer to this - in your second scenario - if your user opens IDP login page directly, they didn't want to go to your app. It's the same if you were using Google or Facebook or one of the other known IDP's for your SPA and as a user I just went to their login page instead. They couldn't possibly know if my intention was to ever come to login so that I am later redirected to your SPA.
Now having said all that - what you could do to make this work somewhat seamless - is to redirect to your SPA's protected page after the user logs in through Identity Server 4 (that's simple because you own the login pages and there is no OAuth involved here). Your SPA would then be triggered to initiate the OAuth2 flow and would redirect back to Identity Server 4. The user has already logged in just seconds ago here though, so the login procedure would be skipped and user would either be presented with consent page or if your client is configured to skip consent page - user would be redirected back to your SPA with the usual tokens and such.
So to break it down into the flow:
User Accesses IDS4 Login Page -> User Enters Credentials -> IDS4
Authenticates User and Redirects to your SPA protected page -> Your
SPA initiates OAuth2 flow and redirects back to IDS4 -> IDS4 displays
consent page -> IDS4 issues auth code back to your SPA.
There is ofcourse extra step here that your SPA will exchange auth code for access token, but I omitted it for clarity purpose as it's not relevant to the question.

Related

Sso on website using winforms and identityserver4

For my company we need a solution for the following simple scenario:
We have a winforms app, on which we login locally.
In this winforms app there is a help button, upon pressing this a web browser should open and the user is authenticated in help site without having to enter credentials again.
The help site uses OpenId-connect to authorize.
We want to use Identityserver as a base.
I have looked into this example: https://github.com/IdentityServer/IdentityServer4/tree/main/samples/Quickstarts/3_AspNetCoreAndApis
The steps here (for openid connect) are:
1 access authorized page on help site
2 site redirects to identityserver login page
3 user logs in and identiyserver redirects to redirectpage with a authorization code
4 help site picks up this auth code and exchanges this for a valid token.
5 user is authenticated
I understand the redirect principle, but having the user to sign in again is annoying.
So what I want to do (I think) is:
1 log into identityserver token endpoint and obtain token using clientid/client secret programmatically in the winforms app
2 Somehow obtain an authorization code to skip the login process from step 2 and
3 open a browser with the url and code from step 3.
Is this somehow possible?
UPDATE i have concluded openid connect is not the right the right way to go for my situation
As Mackie said in last comment, openid connect is not the right solution for this. I'm looking at other technologies like one time password link to achieve what we need. Thank you Mackie

IdenityServer4 - doesn't redirect after MFA

My Auth Server uses IdentityServer4.
Redirect configured as follows for a client
RedirectUris = new List<string>
{
"https://localhost:44342/signin-oidc"
}
this works fine for those users for whom MFA is not enabled. But when it is enabled, and kicks in, the redirect doesn't work. After successful 2nd FA, user stays back on the AuthServer page.
Any idea why?
Multifactor authentication is not implemented by Identityserver4. Identityserver4 is about how the third party application gets access to protected resources on behalf of the user.
The means of how the user gets authenticated are out of the identityserver4 scope. In other words, this is not related to identityserver4.
If you're using the identityserver4 quickstart it comes with ASPNET Identity, ASPNET Identity provides you with a local authentication system for ASPNET applications. MultiFactor Authentication is probably there.
Being said that, when you try to POST to the /authorize endpoint (note authorize not authenticate) from your client application IdentityServer tries to authorize your request and to do so it makes you authenticate first, by presenting you the Login Form.
If you look at the Address bar on this point, you'll notice there's an encoded url as returnUrl param, on the controller code you'll see a check that if that param is present, redirect to that url after successful login.
So, check the flow on your application and see where does that parameter get lost on the redirect hell, at some point you're not passing the returnUrl.

how to integrate regular username/password login with 3rd party social login for a Spring Boot + Angular single page web app?

I have a Angular + Spring boot single page web app. The server also acts as an Auth Server which issues tokens for the angular app to use to make Restful API calls.
My old login flow uses a grant_type=password POST call to the /oauth/token endpoint to get a Bearer token. And all further API calls on behalf of the user will include the Bearer token as the "Authorization" http header.
Now I need to integrate social login (facebook, twitter, etc.), which means I don't have username/password to generate tokens so I'm not sure how to make it work.
I have been using the following two tutorials as my template:
Spring Security and Angular JS
Spring Boot and OAuth
In the first tutorial's oauth-vanilla example, the username passwork login flow brings up the authorization page. But I'd like to have the traditional username/password form login experience (log user in directly instead of showing the Authorization page).
In the second tutorial, after facebook login, I'd like to use the facebook id to look up my internal user database and create a new user if not exist and logs him in as the user. And use the internal db user's identity and authorities to authorize future API calls to my API server.
I have a stripped down sample at at
https://github.com/dingquan/spring-angular-oauth
I can make POST calls to /oauth/token endpoint and use the returned token to make further api calls to my protected /api/blogs endpoint. But I haven't figure out how to make the following things work:
Username/password login that will create a session cookie so I don't need to send the Authorization bearer token for future API calls to the resource endpoint
After facebook login (the facebook login link is under the username/password login form), calls to my endpoint still fails with 401 error (I have a "test" button that makes a get call to /api/blogs, you can click on it to see the behavior). So what am I missing to make the API call succeed?
=== UPDATE ===
Just to clarify. Here are the goals I'm trying to achieve:
multiple ways of authentication (traditional username/password, third party oauth login such as facebook, possibly cellphone number + SMS code in the future)
we do need our own user model backed by DB to store other user attributes, pure social login is not enough
social login needs to be implicit. Meaning user should not be required to create a user account in our system manually once they login through a 3rd party (facebook, etc.). We're not just grabbing users' social profile data to pre-populate the registration form. We want to create new DB users automatically behind the scene if no existing db user is associated with the given external social account. i.e. if user is logged in through facebook, they don't need to enter username/password. Authentication through facebook will automatically log the user into our system as well and user should be able to access restricted resources after facebook login.
There's some confusion that I might be asking people to put their facebook username/password in a login form hosted by my app and I'll login facebook on behalf of the user. That's not what I was asking for.
You don't need such a complicated configuration. Add #EnableOAuth2Sso to your MainConfiguration and set appropriate application properties.
Here is what I have done in order to use Facebook as a authorization server.
a) Remove clientId and authServer from UserServiceImpl. Otherwise you'll be forced to configure an authorization server that is not needed.
b) Remove AuthorizationServerConfiguration completely.
c) Add #EnableWebSecurity and #EnableOAuth2Sso to your MainConfiguration.
d) Change MainConfiguration::configure to
http
.logout().logoutSuccessUrl("/").permitAll().and()
.authorizeRequests().antMatchers("/", "/login", "/home.html").permitAll()
.anyRequest().authenticated()
.and().csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
e) Delete everything else except nested class AuthenticationSecurity from MainConfiguration.
f) Change ResourceServerConfiguration::configure(HttpSecurity) to
http.antMatcher("/api/**").authorizeRequests().anyRequest().authenticated();
f) Remove attribute tokenStore and method ResourceServerConfiguration::configure(ResourceServerSecurityConfigurer) from ResourceServerConfiguration.
g) Remove configuration block security and facebook from application.yml. Instead add this
security:
oauth2:
client:
client-id: <CLIENT_ID>
token-name: oauth_token
authentication-scheme: query
client-authentication-scheme: form
access-token-uri: https://graph.facebook.com/oauth/access_token
user-authorization-uri: https://www.facebook.com/dialog/oauth
resource:
user-info-uri: https://graph.facebook.com/me
client-id: <CLIENT_ID>
client-secret: <CLIENT_SECRET>
token-type: code
h) In index.html change login to login.
i) Replace the content of hello.js with this one.
But I'd like to have the traditional username/password form login experience (log user in directly instead of showing the Authorization page).
I would never use a site that requires my credentials without redirecting me to the origin! I don't know you and you are under suspicion being a phishing site.
You should really reconsider your decision.
Btw, I created a pull request with these changes.

Renew a Long-Lived token used at server side with an Angular application and FB SDK

My context:
An AngularJS application using the Javascript Facebook SDK, and my distinct server (REST APIs).
Workflow:
User is logged in the client through the FB SDK using the method FB.login(callback).
This later gives a short-lived token that is then sent to the server in order to transform it to a long-lived token.
I'm interested in the mechanism of refreshing the long-lived token after 60 days.
So, reading the doc, we found this:
Even the long-lived access token will eventually expire. At any point,
you can generate a new long-lived token by sending the person back to
the login flow used by your web app - note that the person will not
actually need to login again, they have already authorized your app,
so they will immediately redirect back to your app from the login flow
with a refreshed token - how this appears to the person will vary
based on the type of login flow that you are using, for example if you
are using the JavaScript SDK, this will take place in the background,
if you are using a server-side flow, the browser will quickly redirect
to the Login Dialog and then automatically and immediately back to
your app again.
If I interpret it well, when user is ALREADY logged in through FB.login(callback), a simple redirect to the Angular Application's login flow would allow to get a new short-lived token.
I imagine that the FB.login is immediately run anew in this case, without user interaction, as written.
I want to test it simply, so what I've done is:
Logged in into the application through FB.login(callback).
Clicked on a dummy link making a simple redirect with: window.location.replace('/');
My application being a single page application, every URL should be considered as the authentication page.
But the FB.login isn't run in the background, as I expected from the doc.
What would be the reason?
Does it work only when the domain making the redirect is distinct from the client? (I just can't test this case right now)
Did I misinterpret the doc?

AngularJS recover user session - cookies or token

I'm developing an AngularJS app used by third part applications. The third part application and my AngularJS application have a common database where users preferences/credentials are stored. User can login from the third part application and, by redirecting the user into my application, I need to maintain the user logged in, without asking a new authentication procedure.
I can't use cookies because the two applications are in two different domains.
I can't pass a session TOKEN (correspondant to the user logged iin) in query parameters due to man in the middle risks.
Is it possible to make a POST request to an angularJS page? Third part app call my AngularJS login page POSTing a token in the body request. My app take the token, verifyies it and log-in the user.
Constraints:
App in different domains;
Maintain user logged in;
No sharing cookies;
Try to prevent man in the middle;
No query parameters;
HTTPS protocol;
web based applications.
Am I missing something in the https protocols/sharing sessions?
Are there other solutions supported by AngularJS?
How can I redirect the user from one application to another and maintaining the user logged in in a simple way? Is there a simple flow I haven't figured out?
AngularJS is based on REST api communications. I can ask for a webpage (GET the webpage), but I can't make a POST to an AngularJS page. Is there a way to pass/share some values from the first application to my second app in a secure way?

Resources