Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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:

  1. 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

  2. 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:

  1. multiple ways of authentication (traditional username/password, third party oauth login such as facebook, possibly cellphone number + SMS code in the future)
  2. we do need our own user model backed by DB to store other user attributes, pure social login is not enough
  3. 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.

like image 236
Quan Ding Avatar asked Oct 08 '16 23:10

Quan Ding


1 Answers

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 <a href="#/login">login</a> to <a href="/login">login</a>. 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.

like image 120
ksokol Avatar answered Nov 17 '22 02:11

ksokol