Google oAuth2 CORS error - Jak powinno wyglądać flow

Odpowiedz Nowy wątek
2019-10-10 11:16
0

Cześć,

Mam problem z połączeniem Spring Boot + Angular z uwierzytelniania z google. (Google oAuth2)

Bez klienta (Angular), gdy robię request na http://localhost:8080/oauth2/authorization/google (wpisuje w adres przeglądarki) to wyświetla mi się okno logowania do google, podaje login i hasło następnie otrzymuje response i w customOidcUserService wyciągam dane i zapisuje obiekt do bazy.

CustomAuthenticationSuccessHandler tu wyszukuje usera w bazie po e-mailu z authentication.getPrincipal() generuje sobie JWT token i robie redirect na /oauth2/authenticate, gdzie zwracam wygenerowany JWT. Ogólnie nie wiem czy to w ogóle dobre podejście.

Gdy robie to z Angulara to dostaję następujący błąd, nie pojawia się też formularz logowania do google:

Access to XMLHttpRequest at 'https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=1020159669873-d9r35ssmnejud852bam87d8gqtcj5qf1.apps.googleusercontent.com&scope=openid%20profile%20email&state=8nizHP1X2z9sA8m0vqM4Lzd6VT24R15eSw5flteTywM%3D&redirect_uri=http://localhost:8080/oauth2/callback/google' (redirected from 'http://localhost:8080/oauth2/authorization/google')
from origin 'null' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Cross-Origin Read Blocking (CORB) blocked cross-origin response https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=1020159669873-d9r35ssmnejud852bam87d8gqtcj5qf1.apps.googleusercontent.com&scope=openid%20profile%20email&state=8nizHP1X2z9sA8m0vqM4Lzd6VT24R15eSw5flteTywM%3D&redirect_uri=http://localhost:8080/oauth2/callback/google with MIME type text/html. See https://www.chromestatus.com/feature/5629709824032768 for more details.

Jak powinno to wyglądać prawidłowo? Czy mógłby mi to ktoś opisać, dlaczego dostaje ten błąd? Czy zapytanie z Angulara powinno iść bezpośrednio do Google? Jakiś pseudokod, albo przykłady? Jak zrobić, żeby to działało?
Znalazłem taki schemat w google:
screenshot-20191010110654.png

Mój kod:
CustomAuthenticationSuccessHandler:

@Component
public class CustomAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private JwtTokenProvider tokenProvider;

    private final static String URL = "http://localhost:8080/api/v1/oauth2/authenticate";

    @Override
    public void onAuthenticationSuccess(
            HttpServletRequest request,
            HttpServletResponse response,
            Authentication authentication) throws IOException, ServletException {

        if (response.isCommitted()) {
            return; }

        DefaultOidcUser oidcUser = (DefaultOidcUser) authentication.getPrincipal();
        System.out.println(oidcUser);
        Map attributes = oidcUser.getAttributes();
        String email = attributes.get("email").toString();
        User user = userRepository.findByEmail(email).orElseThrow(
                () -> new ResourceNotFoundException("User", "email", email)
        );
        String token = tokenProvider.generateToken(user);
        String redirectionUrl = UriComponentsBuilder.fromUriString(URL).queryParam("token", token)
                .build().toUriString();
        getRedirectStrategy().sendRedirect(request, response, redirectionUrl);
    }
}

Controller:

@RestController
@RequestMapping("/api/v1/oauth2")
public class OAuth2Controller {

    @GetMapping("/authenticate")
    public ResponseEntity<?> authenticateUser(@RequestParam String token) {
        return ResponseEntity.ok(new JwtAuthenticationResponse(token));
    }
}

Konfiguracja:

@Configuration
@EnableWebSecurity(debug = true)
@EnableGlobalMethodSecurity(
        securedEnabled = true,
        jsr250Enabled = true,
        prePostEnabled = true
)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomUserDetailsService customUserDetailsService;

    @Autowired
    private JwtAuthenticationEntryPoint unauthorizedHandler;

    @Bean
    public JwtAuthenticationFilter jwtAuthenticationFilter() {
        return new JwtAuthenticationFilter();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean(BeanIds.AUTHENTICATION_MANAGER)
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Autowired
    private CustomOidcUserService customOidcUserService;

    @Autowired
    private CustomAuthenticationSuccessHandler successHandler;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .userDetailsService(customUserDetailsService)
                .passwordEncoder(passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .cors()
                    .and()
                .csrf()
                    .disable()
                .exceptionHandling()
                    .authenticationEntryPoint(unauthorizedHandler)
                    .and()
                .sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    .and()
                .authorizeRequests()
                    .antMatchers("/",
                "/favicon.ico",
                "/**/*.png",
                "/**/*.gif",
                "/**/*.svg",
                "/**/*.jpg",
                "/**/*.html",
                "/**/*.css",
                "/**/*.js")
                        .permitAll()
                .antMatchers("/api/v1/oauth0/**")
                        .permitAll()
                .antMatchers("/api/v1/oauth2/**")
                    .permitAll()
                .anyRequest()
                    .authenticated()
                    .and()
                // włączenie obslugi oauth2
                .oauth2Login()
                .successHandler(this.successHandler)
                .redirectionEndpoint()
                    .baseUri("/oauth2/callback/*")
                    .and()
                .userInfoEndpoint()
                    .oidcUserService(customOidcUserService);
        http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    }
}

Angular:

googleLogin(): Observable<LoginResponse> {
    return this.http.get<LoginResponse>
    (environment.baseUrl + '/oauth2/authorization/google')
      .pipe(tap(response => {
        localStorage.setItem('access_token', response.accessToken);
      }));
  }

Dzięki za pomoc i ogólne nakreślenie sprawy.

edytowany 4x, ostatnio: tomaszek993, 2019-10-10 11:20
staraj się nie robić autowired na polach jeśli nie jest to konieczne - danek 2019-10-10 12:36
@danek: Tak, zrobię to później na konstruktorze. Tylko w konfiguracji zostawię jak jest. - tomaszek993 2019-10-10 12:40

Pozostało 580 znaków

2019-10-10 11:22

W skrócie: nie wolno ci stuknąć tego: https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=1020159669873-d9r35ssmnejud852bam87d8gqtcj5qf1.apps.googleusercontent.com&scope=openid%20profile%20email&state=8nizHP1X2z9sA8m0vqM4Lzd6VT24R15eSw5flteTywM%3D&redirect_uri=http://localhost:8080/oauth2/callback/google ajaxem, to musi być legitny GET od usera. Czyli u ciebie nie możesz stukać http://localhost:8080/oauth2/authorization/google żadnym ajaxem, musisz faktycznie otworzyć taki link userowi. Co zresztą ma przecież sens, bo userowi pojawi się formularz który musi wypełnić (login/hasło albo wybór konta google).


Masz problem? Pisz na forum, nie do mnie. Nie masz problemów? Kup komputer...
edytowany 2x, ostatnio: Shalom, 2019-10-10 11:23
Pokaż pozostałe 2 komentarze
A co do reszty pytania takie podejście na backendzie jest w ogóle akceptowalne ;) ? Przesyłam tam token w queryParams... - tomaszek993 2019-10-10 11:38
@Shalom: Jak otworzę jako zwykły link to wyświetla mi się okno logowania wpisuje dane wszystko leci dalej dostaję JWT od serwera i zostaję na stronie serwera nie wracam do klienta. Ja mam jakoś po stronie serwera zrobić z powrotem przekierowanie do klienta? Chyba nie czaję jak do powinno działać. - tomaszek993 2019-10-10 17:35
Nie rozumiem. OAuth googlowy przekierowuje cię przecież do redirect_uri=http://localhost:8080/oauth2/callback/google i wrzuca ci tam ten jakiś userCode. Ty sobie z niego możesz pogenerować JWT i wrócić usera gdzie chcesz. Gdzie ty masz problem? - Shalom 2019-10-10 17:39
@Shalom: W kwestii, czy przekierowanie z serwera na klienta robiąc po prostu redirect na (localhost:4200/*) jest ok? Nie wiem robię autoryzację z Google to pierwszy raz. Taka dziewicza akcja :D - tomaszek993 2019-10-10 17:43
No możesz tak zrobić, a możesz też zrobić tak ze to redirect_uri które idzie do oauth od razu kieruje usera do twojego UI, a ty sobie tam ogarniasz te parametry które google zwraca i stukasz do swojego backendu po token. - Shalom 2019-10-10 17:50

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0