├── angular-11-social-login
├── src
│ ├── assets
│ │ ├── .gitkeep
│ │ └── img
│ │ │ ├── github.png
│ │ │ ├── google.png
│ │ │ ├── logo.png
│ │ │ ├── facebook.png
│ │ │ └── linkedin.png
│ ├── app
│ │ ├── app.component.css
│ │ ├── home
│ │ │ ├── home.component.css
│ │ │ ├── home.component.html
│ │ │ ├── home.component.ts
│ │ │ └── home.component.spec.ts
│ │ ├── profile
│ │ │ ├── profile.component.css
│ │ │ ├── profile.component.html
│ │ │ ├── profile.component.ts
│ │ │ └── profile.component.spec.ts
│ │ ├── board-user
│ │ │ ├── board-user.component.css
│ │ │ ├── board-user.component.html
│ │ │ ├── board-user.component.ts
│ │ │ └── board-user.component.spec.ts
│ │ ├── board-admin
│ │ │ ├── board-admin.component.css
│ │ │ ├── board-admin.component.html
│ │ │ ├── board-admin.component.ts
│ │ │ └── board-admin.component.spec.ts
│ │ ├── board-moderator
│ │ │ ├── board-moderator.component.css
│ │ │ ├── board-moderator.component.html
│ │ │ ├── board-moderator.component.ts
│ │ │ └── board-moderator.component.spec.ts
│ │ ├── _services
│ │ │ ├── auth.service.spec.ts
│ │ │ ├── user.service.spec.ts
│ │ │ ├── token-storage.service.spec.ts
│ │ │ ├── token-storage.service.ts
│ │ │ ├── user.service.ts
│ │ │ └── auth.service.ts
│ │ ├── totp
│ │ │ ├── totp.component.css
│ │ │ ├── totp.component.html
│ │ │ └── totp.component.ts
│ │ ├── login
│ │ │ ├── login.component.spec.ts
│ │ │ ├── login.component.css
│ │ │ ├── login.component.html
│ │ │ └── login.component.ts
│ │ ├── register
│ │ │ ├── register.component.spec.ts
│ │ │ ├── register.component.css
│ │ │ ├── register.component.ts
│ │ │ └── register.component.html
│ │ ├── common
│ │ │ └── app.constants.ts
│ │ ├── app.component.ts
│ │ ├── app.component.spec.ts
│ │ ├── app.component.html
│ │ ├── app-routing.module.ts
│ │ ├── _helpers
│ │ │ └── auth.interceptor.ts
│ │ └── app.module.ts
│ ├── environments
│ │ ├── environment.prod.ts
│ │ └── environment.ts
│ ├── styles.css
│ ├── favicon.ico
│ ├── main.ts
│ ├── index.html
│ ├── test.ts
│ └── polyfills.ts
├── .dockerignore
├── nginx-custom.conf
├── .editorconfig
├── e2e
│ ├── src
│ │ ├── app.po.ts
│ │ └── app.e2e-spec.ts
│ ├── tsconfig.json
│ └── protractor.conf.js
├── tsconfig.app.json
├── tsconfig.spec.json
├── tsconfig.json
├── tsconfig.base.json
├── .browserslistrc
├── README.md
├── .gitignore
├── karma.conf.js
├── Dockerfile
├── package.json
├── tslint.json
└── angular.json
├── spring-boot-oauth2-social-login
├── src
│ └── main
│ │ ├── resources
│ │ ├── messages_en.properties
│ │ └── application.properties
│ │ └── java
│ │ └── com
│ │ └── javachinna
│ │ ├── dto
│ │ ├── ApiResponse.java
│ │ ├── SignUpResponse.java
│ │ ├── UserInfo.java
│ │ ├── JwtAuthenticationResponse.java
│ │ ├── LoginRequest.java
│ │ ├── SocialProvider.java
│ │ ├── SignUpRequest.java
│ │ └── LocalUser.java
│ │ ├── repo
│ │ ├── RoleRepository.java
│ │ └── UserRepository.java
│ │ ├── exception
│ │ ├── UserAlreadyExistAuthenticationException.java
│ │ ├── OAuth2AuthenticationProcessingException.java
│ │ ├── BadRequestException.java
│ │ ├── ResourceNotFoundException.java
│ │ └── handler
│ │ │ └── RestResponseEntityExceptionHandler.java
│ │ ├── validator
│ │ ├── PasswordMatchesValidator.java
│ │ └── PasswordMatches.java
│ │ ├── config
│ │ ├── CurrentUser.java
│ │ ├── RestAuthenticationEntryPoint.java
│ │ ├── AppProperties.java
│ │ ├── WebConfig.java
│ │ ├── SetupDataLoader.java
│ │ └── WebSecurityConfig.java
│ │ ├── security
│ │ ├── oauth2
│ │ │ ├── user
│ │ │ │ ├── OAuth2UserInfo.java
│ │ │ │ ├── GoogleOAuth2UserInfo.java
│ │ │ │ ├── GithubOAuth2UserInfo.java
│ │ │ │ ├── LinkedinOAuth2UserInfo.java
│ │ │ │ ├── FacebookOAuth2UserInfo.java
│ │ │ │ └── OAuth2UserInfoFactory.java
│ │ │ ├── CustomOidcUserService.java
│ │ │ ├── OAuth2AuthenticationFailureHandler.java
│ │ │ ├── HttpCookieOAuth2AuthorizationRequestRepository.java
│ │ │ ├── OAuth2AccessTokenResponseConverterWithDefaults.java
│ │ │ ├── CustomOAuth2UserService.java
│ │ │ └── OAuth2AuthenticationSuccessHandler.java
│ │ └── jwt
│ │ │ ├── TokenProvider.java
│ │ │ └── TokenAuthenticationFilter.java
│ │ ├── service
│ │ ├── UserService.java
│ │ ├── LocalUserDetailService.java
│ │ └── UserServiceImpl.java
│ │ ├── DemoApplication.java
│ │ ├── util
│ │ ├── GeneralUtils.java
│ │ └── CookieUtils.java
│ │ ├── controller
│ │ ├── UserController.java
│ │ └── AuthController.java
│ │ └── model
│ │ ├── User.java
│ │ └── Role.java
├── .mvn
│ └── wrapper
│ │ ├── maven-wrapper.jar
│ │ ├── maven-wrapper.properties
│ │ └── MavenWrapperDownloader.java
├── Dockerfile
├── pom.xml
├── mvnw.cmd
└── mvnw
├── README.md
├── LICENSE
└── docker-compose.yml
/angular-11-social-login/src/assets/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/app.component.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/home/home.component.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/profile/profile.component.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/board-user/board-user.component.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/angular-11-social-login/.dockerignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .git
3 | .gitignore
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/board-admin/board-admin.component.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/board-moderator/board-moderator.component.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/styles.css:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/home/home.component.html:
--------------------------------------------------------------------------------
1 |
2 |
{{ content }}
3 |
4 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/board-user/board-user.component.html:
--------------------------------------------------------------------------------
1 |
2 |
{{ content }}
3 |
4 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/board-admin/board-admin.component.html:
--------------------------------------------------------------------------------
1 |
2 |
{{ content }}
3 |
4 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/board-moderator/board-moderator.component.html:
--------------------------------------------------------------------------------
1 |
2 |
{{ content }}
3 |
4 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JavaChinna/spring-boot-angular-2fa-demo/HEAD/angular-11-social-login/src/favicon.ico
--------------------------------------------------------------------------------
/angular-11-social-login/src/assets/img/github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JavaChinna/spring-boot-angular-2fa-demo/HEAD/angular-11-social-login/src/assets/img/github.png
--------------------------------------------------------------------------------
/angular-11-social-login/src/assets/img/google.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JavaChinna/spring-boot-angular-2fa-demo/HEAD/angular-11-social-login/src/assets/img/google.png
--------------------------------------------------------------------------------
/angular-11-social-login/src/assets/img/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JavaChinna/spring-boot-angular-2fa-demo/HEAD/angular-11-social-login/src/assets/img/logo.png
--------------------------------------------------------------------------------
/spring-boot-oauth2-social-login/src/main/resources/messages_en.properties:
--------------------------------------------------------------------------------
1 | NotEmpty=This field is required.
2 | Size.userDto.password=Try one with at least 6 characters.
3 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/assets/img/facebook.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JavaChinna/spring-boot-angular-2fa-demo/HEAD/angular-11-social-login/src/assets/img/facebook.png
--------------------------------------------------------------------------------
/angular-11-social-login/src/assets/img/linkedin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JavaChinna/spring-boot-angular-2fa-demo/HEAD/angular-11-social-login/src/assets/img/linkedin.png
--------------------------------------------------------------------------------
/angular-11-social-login/nginx-custom.conf:
--------------------------------------------------------------------------------
1 | server {
2 | listen 80;
3 | location / {
4 | root /usr/share/nginx/html;
5 | index index.html index.htm;
6 | try_files $uri $uri/ /index.html =404;
7 | }
8 | }
--------------------------------------------------------------------------------
/spring-boot-oauth2-social-login/.mvn/wrapper/maven-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JavaChinna/spring-boot-angular-2fa-demo/HEAD/spring-boot-oauth2-social-login/.mvn/wrapper/maven-wrapper.jar
--------------------------------------------------------------------------------
/spring-boot-oauth2-social-login/src/main/java/com/javachinna/dto/ApiResponse.java:
--------------------------------------------------------------------------------
1 | package com.javachinna.dto;
2 |
3 | import lombok.Value;
4 |
5 | @Value
6 | public class ApiResponse {
7 | private Boolean success;
8 | private String message;
9 | }
--------------------------------------------------------------------------------
/spring-boot-oauth2-social-login/src/main/java/com/javachinna/dto/SignUpResponse.java:
--------------------------------------------------------------------------------
1 | package com.javachinna.dto;
2 |
3 | import lombok.Value;
4 |
5 | @Value
6 | public class SignUpResponse {
7 | private boolean using2FA;
8 | private String qrCodeImage;
9 | }
--------------------------------------------------------------------------------
/spring-boot-oauth2-social-login/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
3 |
--------------------------------------------------------------------------------
/spring-boot-oauth2-social-login/src/main/java/com/javachinna/dto/UserInfo.java:
--------------------------------------------------------------------------------
1 | package com.javachinna.dto;
2 |
3 | import java.util.List;
4 |
5 | import lombok.Value;
6 |
7 | @Value
8 | public class UserInfo {
9 | private String id, displayName, email;
10 | private List roles;
11 | }
--------------------------------------------------------------------------------
/spring-boot-oauth2-social-login/src/main/java/com/javachinna/dto/JwtAuthenticationResponse.java:
--------------------------------------------------------------------------------
1 | package com.javachinna.dto;
2 |
3 | import lombok.Value;
4 |
5 | @Value
6 | public class JwtAuthenticationResponse {
7 | private String accessToken;
8 | private boolean authenticated;
9 | private UserInfo user;
10 | }
--------------------------------------------------------------------------------
/spring-boot-oauth2-social-login/src/main/java/com/javachinna/dto/LoginRequest.java:
--------------------------------------------------------------------------------
1 | package com.javachinna.dto;
2 |
3 | import javax.validation.constraints.NotBlank;
4 |
5 | import lombok.Data;
6 |
7 | @Data
8 | public class LoginRequest {
9 | @NotBlank
10 | private String email;
11 |
12 | @NotBlank
13 | private String password;
14 | }
--------------------------------------------------------------------------------
/angular-11-social-login/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see https://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.ts]
12 | quote_type = single
13 |
14 | [*.md]
15 | max_line_length = off
16 | trim_trailing_whitespace = false
17 |
--------------------------------------------------------------------------------
/angular-11-social-login/e2e/src/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, by, element } from 'protractor';
2 |
3 | export class AppPage {
4 | navigateTo(): Promise {
5 | return browser.get(browser.baseUrl) as Promise;
6 | }
7 |
8 | getTitleText(): Promise {
9 | return element(by.css('app-root .content span')).getText() as Promise;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/angular-11-social-login/e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "../tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "../out-tsc/e2e",
6 | "module": "commonjs",
7 | "target": "es2018",
8 | "types": [
9 | "jasmine",
10 | "jasminewd2",
11 | "node"
12 | ]
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/angular-11-social-login/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "./tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "./out-tsc/app",
6 | "types": []
7 | },
8 | "files": [
9 | "src/main.ts",
10 | "src/polyfills.ts"
11 | ],
12 | "include": [
13 | "src/**/*.d.ts"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/spring-boot-oauth2-social-login/src/main/java/com/javachinna/repo/RoleRepository.java:
--------------------------------------------------------------------------------
1 | package com.javachinna.repo;
2 |
3 | import org.springframework.data.jpa.repository.JpaRepository;
4 | import org.springframework.stereotype.Repository;
5 |
6 | import com.javachinna.model.Role;
7 |
8 | @Repository
9 | public interface RoleRepository extends JpaRepository {
10 |
11 | Role findByName(String name);
12 | }
13 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/main.ts:
--------------------------------------------------------------------------------
1 | import { enableProdMode } from '@angular/core';
2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
3 |
4 | import { AppModule } from './app/app.module';
5 | import { environment } from './environments/environment';
6 |
7 | if (environment.production) {
8 | enableProdMode();
9 | }
10 |
11 | platformBrowserDynamic().bootstrapModule(AppModule)
12 | .catch(err => console.error(err));
13 |
--------------------------------------------------------------------------------
/angular-11-social-login/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "./tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "./out-tsc/spec",
6 | "types": [
7 | "jasmine"
8 | ]
9 | },
10 | "files": [
11 | "src/test.ts",
12 | "src/polyfills.ts"
13 | ],
14 | "include": [
15 | "src/**/*.spec.ts",
16 | "src/**/*.d.ts"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/_services/auth.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed } from '@angular/core/testing';
2 |
3 | import { AuthService } from './auth.service';
4 |
5 | describe('AuthService', () => {
6 | let service: AuthService;
7 |
8 | beforeEach(() => {
9 | TestBed.configureTestingModule({});
10 | service = TestBed.inject(AuthService);
11 | });
12 |
13 | it('should be created', () => {
14 | expect(service).toBeTruthy();
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/_services/user.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed } from '@angular/core/testing';
2 |
3 | import { UserService } from './user.service';
4 |
5 | describe('UserService', () => {
6 | let service: UserService;
7 |
8 | beforeEach(() => {
9 | TestBed.configureTestingModule({});
10 | service = TestBed.inject(UserService);
11 | });
12 |
13 | it('should be created', () => {
14 | expect(service).toBeTruthy();
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/spring-boot-oauth2-social-login/src/main/java/com/javachinna/repo/UserRepository.java:
--------------------------------------------------------------------------------
1 | package com.javachinna.repo;
2 |
3 | import org.springframework.data.jpa.repository.JpaRepository;
4 | import org.springframework.stereotype.Repository;
5 |
6 | import com.javachinna.model.User;
7 |
8 | @Repository
9 | public interface UserRepository extends JpaRepository {
10 |
11 | User findByEmail(String email);
12 |
13 | boolean existsByEmail(String email);
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/profile/profile.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{ currentUser.displayName }} Profile
5 |
6 |
7 |
8 | Email: {{ currentUser.email }}
9 |
10 |
Roles:
11 |
14 |
15 | Please login.
16 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/_services/token-storage.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed } from '@angular/core/testing';
2 |
3 | import { TokenStorageService } from './token-storage.service';
4 |
5 | describe('TokenStorageService', () => {
6 | let service: TokenStorageService;
7 |
8 | beforeEach(() => {
9 | TestBed.configureTestingModule({});
10 | service = TestBed.inject(TokenStorageService);
11 | });
12 |
13 | it('should be created', () => {
14 | expect(service).toBeTruthy();
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/spring-boot-oauth2-social-login/src/main/java/com/javachinna/dto/SocialProvider.java:
--------------------------------------------------------------------------------
1 | package com.javachinna.dto;
2 |
3 | /**
4 | * @author Chinna
5 | * @since 26/3/18
6 | */
7 | public enum SocialProvider {
8 |
9 | FACEBOOK("facebook"), TWITTER("twitter"), LINKEDIN("linkedin"), GOOGLE("google"), GITHUB("github"), LOCAL("local");
10 |
11 | private String providerType;
12 |
13 | public String getProviderType() {
14 | return providerType;
15 | }
16 |
17 | SocialProvider(final String providerType) {
18 | this.providerType = providerType;
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/angular-11-social-login/tsconfig.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "compileOnSave": false,
4 | "compilerOptions": {
5 | "baseUrl": "./",
6 | "outDir": "./dist/out-tsc",
7 | "sourceMap": true,
8 | "declaration": false,
9 | "downlevelIteration": true,
10 | "experimentalDecorators": true,
11 | "moduleResolution": "node",
12 | "importHelpers": true,
13 | "target": "es2015",
14 | "module": "es2020",
15 | "lib": [
16 | "es2018",
17 | "dom"
18 | ]
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/angular-11-social-login/tsconfig.base.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "compileOnSave": false,
4 | "compilerOptions": {
5 | "baseUrl": "./",
6 | "outDir": "./dist/out-tsc",
7 | "sourceMap": true,
8 | "declaration": false,
9 | "downlevelIteration": true,
10 | "experimentalDecorators": true,
11 | "moduleResolution": "node",
12 | "importHelpers": true,
13 | "target": "es2015",
14 | "module": "es2020",
15 | "lib": [
16 | "es2018",
17 | "dom"
18 | ]
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/profile/profile.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { TokenStorageService } from '../_services/token-storage.service';
3 |
4 | @Component({
5 | selector: 'app-profile',
6 | templateUrl: './profile.component.html',
7 | styleUrls: ['./profile.component.css']
8 | })
9 | export class ProfileComponent implements OnInit {
10 |
11 | currentUser: any;
12 |
13 | constructor(private token: TokenStorageService) { }
14 |
15 | ngOnInit(): void {
16 | this.currentUser = this.token.getUser();
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/spring-boot-oauth2-social-login/src/main/java/com/javachinna/exception/UserAlreadyExistAuthenticationException.java:
--------------------------------------------------------------------------------
1 | package com.javachinna.exception;
2 |
3 | import org.springframework.security.core.AuthenticationException;
4 |
5 | /**
6 | *
7 | * @author Chinna
8 | *
9 | */
10 | public class UserAlreadyExistAuthenticationException extends AuthenticationException {
11 |
12 | /**
13 | *
14 | */
15 | private static final long serialVersionUID = 5570981880007077317L;
16 |
17 | public UserAlreadyExistAuthenticationException(final String msg) {
18 | super(msg);
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/spring-boot-oauth2-social-login/src/main/java/com/javachinna/validator/PasswordMatchesValidator.java:
--------------------------------------------------------------------------------
1 | package com.javachinna.validator;
2 |
3 | import javax.validation.ConstraintValidator;
4 | import javax.validation.ConstraintValidatorContext;
5 |
6 | import com.javachinna.dto.SignUpRequest;
7 |
8 | public class PasswordMatchesValidator implements ConstraintValidator {
9 |
10 | @Override
11 | public boolean isValid(final SignUpRequest user, final ConstraintValidatorContext context) {
12 | return user.getPassword().equals(user.getMatchingPassword());
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/spring-boot-oauth2-social-login/src/main/java/com/javachinna/config/CurrentUser.java:
--------------------------------------------------------------------------------
1 | package com.javachinna.config;
2 |
3 | import java.lang.annotation.Documented;
4 | import java.lang.annotation.ElementType;
5 | import java.lang.annotation.Retention;
6 | import java.lang.annotation.RetentionPolicy;
7 | import java.lang.annotation.Target;
8 |
9 | import org.springframework.security.core.annotation.AuthenticationPrincipal;
10 |
11 | @Target({ ElementType.PARAMETER, ElementType.TYPE })
12 | @Retention(RetentionPolicy.RUNTIME)
13 | @Documented
14 | @AuthenticationPrincipal
15 | public @interface CurrentUser {
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/spring-boot-oauth2-social-login/src/main/java/com/javachinna/exception/OAuth2AuthenticationProcessingException.java:
--------------------------------------------------------------------------------
1 | package com.javachinna.exception;
2 |
3 | import org.springframework.security.core.AuthenticationException;
4 |
5 | public class OAuth2AuthenticationProcessingException extends AuthenticationException {
6 | /**
7 | *
8 | */
9 | private static final long serialVersionUID = 3392450042101522832L;
10 |
11 | public OAuth2AuthenticationProcessingException(String msg, Throwable t) {
12 | super(msg, t);
13 | }
14 |
15 | public OAuth2AuthenticationProcessingException(String msg) {
16 | super(msg);
17 | }
18 | }
--------------------------------------------------------------------------------
/spring-boot-oauth2-social-login/src/main/java/com/javachinna/exception/BadRequestException.java:
--------------------------------------------------------------------------------
1 | package com.javachinna.exception;
2 |
3 | import org.springframework.http.HttpStatus;
4 | import org.springframework.web.bind.annotation.ResponseStatus;
5 |
6 | @ResponseStatus(HttpStatus.BAD_REQUEST)
7 | public class BadRequestException extends RuntimeException {
8 | /**
9 | *
10 | */
11 | private static final long serialVersionUID = 752858877580882829L;
12 |
13 | public BadRequestException(String message) {
14 | super(message);
15 | }
16 |
17 | public BadRequestException(String message, Throwable cause) {
18 | super(message, cause);
19 | }
20 | }
--------------------------------------------------------------------------------
/spring-boot-oauth2-social-login/src/main/java/com/javachinna/security/oauth2/user/OAuth2UserInfo.java:
--------------------------------------------------------------------------------
1 | package com.javachinna.security.oauth2.user;
2 |
3 | import java.util.Map;
4 |
5 | public abstract class OAuth2UserInfo {
6 | protected Map attributes;
7 |
8 | public OAuth2UserInfo(Map attributes) {
9 | this.attributes = attributes;
10 | }
11 |
12 | public Map getAttributes() {
13 | return attributes;
14 | }
15 |
16 | public abstract String getId();
17 |
18 | public abstract String getName();
19 |
20 | public abstract String getEmail();
21 |
22 | public abstract String getImageUrl();
23 | }
--------------------------------------------------------------------------------
/angular-11-social-login/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Angular10SocialLogin
6 |
7 |
8 |
9 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/home/home.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { UserService } from '../_services/user.service';
3 |
4 | @Component({
5 | selector: 'app-home',
6 | templateUrl: './home.component.html',
7 | styleUrls: ['./home.component.css']
8 | })
9 | export class HomeComponent implements OnInit {
10 |
11 | content: string;
12 |
13 | constructor(private userService: UserService) { }
14 |
15 | ngOnInit(): void {
16 | this.userService.getPublicContent().subscribe(
17 | data => {
18 | this.content = data;
19 | },
20 | err => {
21 | this.content = JSON.parse(err.error).message;
22 | }
23 | );
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/angular-11-social-login/.browserslistrc:
--------------------------------------------------------------------------------
1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
2 | # For additional information regarding the format and rule options, please see:
3 | # https://github.com/browserslist/browserslist#queries
4 |
5 | # For the full list of supported browsers by the Angular framework, please see:
6 | # https://angular.io/guide/browser-support
7 |
8 | # You can see what browsers were selected by your queries by running:
9 | # npx browserslist
10 |
11 | last 1 Chrome version
12 | last 1 Firefox version
13 | last 2 Edge major versions
14 | last 2 Safari major version
15 | last 2 iOS major versions
16 | Firefox ESR
17 | not IE 9-11 # For IE 9-11 support, remove 'not'.
18 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/totp/totp.component.css:
--------------------------------------------------------------------------------
1 | .card-container.card {
2 | max-width: 400px !important;
3 | padding: 40px 40px;
4 | }
5 |
6 | .card {
7 | background-color: #f7f7f7;
8 | padding: 20px 25px 30px;
9 | margin: 0 auto 25px;
10 | margin-top: 50px;
11 | -moz-border-radius: 2px;
12 | -webkit-border-radius: 2px;
13 | border-radius: 2px;
14 | -moz-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
15 | -webkit-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
16 | box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
17 | }
18 |
19 | .profile-img-card {
20 | width: 96px;
21 | height: 96px;
22 | margin: 0 auto 10px;
23 | display: block;
24 | -moz-border-radius: 50%;
25 | -webkit-border-radius: 50%;
26 | border-radius: 50%;
27 | }
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/board-user/board-user.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { UserService } from '../_services/user.service';
3 |
4 | @Component({
5 | selector: 'app-board-user',
6 | templateUrl: './board-user.component.html',
7 | styleUrls: ['./board-user.component.css']
8 | })
9 | export class BoardUserComponent implements OnInit {
10 |
11 | content: string;
12 |
13 | constructor(private userService: UserService) { }
14 |
15 | ngOnInit(): void {
16 | this.userService.getUserBoard().subscribe(
17 | data => {
18 | this.content = data;
19 | },
20 | err => {
21 | this.content = JSON.parse(err.error).message;
22 | }
23 | );
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/board-admin/board-admin.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { UserService } from '../_services/user.service';
3 |
4 | @Component({
5 | selector: 'app-board-admin',
6 | templateUrl: './board-admin.component.html',
7 | styleUrls: ['./board-admin.component.css']
8 | })
9 | export class BoardAdminComponent implements OnInit {
10 |
11 | content: string;
12 |
13 | constructor(private userService: UserService) { }
14 |
15 | ngOnInit(): void {
16 | this.userService.getAdminBoard().subscribe(
17 | data => {
18 | this.content = data;
19 | },
20 | err => {
21 | this.content = JSON.parse(err.error).message;
22 | }
23 | );
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // This file can be replaced during build by using the `fileReplacements` array.
2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
3 | // The list of file replacements can be found in `angular.json`.
4 |
5 | export const environment = {
6 | production: false
7 | };
8 |
9 | /*
10 | * For easier debugging in development mode, you can import the following file
11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
12 | *
13 | * This import should be commented out in production mode because it will have a negative impact
14 | * on performance if an error is thrown.
15 | */
16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI.
17 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/home/home.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
2 |
3 | import { HomeComponent } from './home.component';
4 |
5 | describe('HomeComponent', () => {
6 | let component: HomeComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(waitForAsync(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ HomeComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(HomeComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/spring-boot-oauth2-social-login/src/main/java/com/javachinna/security/oauth2/user/GoogleOAuth2UserInfo.java:
--------------------------------------------------------------------------------
1 | package com.javachinna.security.oauth2.user;
2 |
3 | import java.util.Map;
4 |
5 | public class GoogleOAuth2UserInfo extends OAuth2UserInfo {
6 |
7 | public GoogleOAuth2UserInfo(Map attributes) {
8 | super(attributes);
9 | }
10 |
11 | @Override
12 | public String getId() {
13 | return (String) attributes.get("sub");
14 | }
15 |
16 | @Override
17 | public String getName() {
18 | return (String) attributes.get("name");
19 | }
20 |
21 | @Override
22 | public String getEmail() {
23 | return (String) attributes.get("email");
24 | }
25 |
26 | @Override
27 | public String getImageUrl() {
28 | return (String) attributes.get("picture");
29 | }
30 | }
--------------------------------------------------------------------------------
/angular-11-social-login/e2e/src/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { AppPage } from './app.po';
2 | import { browser, logging } from 'protractor';
3 |
4 | describe('workspace-project App', () => {
5 | let page: AppPage;
6 |
7 | beforeEach(() => {
8 | page = new AppPage();
9 | });
10 |
11 | it('should display welcome message', () => {
12 | page.navigateTo();
13 | expect(page.getTitleText()).toEqual('Angular10SocialLogin app is running!');
14 | });
15 |
16 | afterEach(async () => {
17 | // Assert that there are no errors emitted from the browser
18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER);
19 | expect(logs).not.toContain(jasmine.objectContaining({
20 | level: logging.Level.SEVERE,
21 | } as logging.Entry));
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/board-moderator/board-moderator.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { UserService } from '../_services/user.service';
3 |
4 | @Component({
5 | selector: 'app-board-moderator',
6 | templateUrl: './board-moderator.component.html',
7 | styleUrls: ['./board-moderator.component.css']
8 | })
9 | export class BoardModeratorComponent implements OnInit {
10 |
11 | content: string;
12 |
13 | constructor(private userService: UserService) { }
14 |
15 | ngOnInit(): void {
16 | this.userService.getModeratorBoard().subscribe(
17 | data => {
18 | this.content = data;
19 | },
20 | err => {
21 | this.content = JSON.parse(err.error).message;
22 | }
23 | );
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/login/login.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
2 |
3 | import { LoginComponent } from './login.component';
4 |
5 | describe('LoginComponent', () => {
6 | let component: LoginComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(waitForAsync(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ LoginComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(LoginComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/spring-boot-oauth2-social-login/src/main/java/com/javachinna/security/oauth2/user/GithubOAuth2UserInfo.java:
--------------------------------------------------------------------------------
1 | package com.javachinna.security.oauth2.user;
2 |
3 | import java.util.Map;
4 |
5 | public class GithubOAuth2UserInfo extends OAuth2UserInfo {
6 |
7 | public GithubOAuth2UserInfo(Map attributes) {
8 | super(attributes);
9 | }
10 |
11 | @Override
12 | public String getId() {
13 | return ((Integer) attributes.get("id")).toString();
14 | }
15 |
16 | @Override
17 | public String getName() {
18 | return (String) attributes.get("name");
19 | }
20 |
21 | @Override
22 | public String getEmail() {
23 | return (String) attributes.get("email");
24 | }
25 |
26 | @Override
27 | public String getImageUrl() {
28 | return (String) attributes.get("avatar_url");
29 | }
30 | }
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/profile/profile.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
2 |
3 | import { ProfileComponent } from './profile.component';
4 |
5 | describe('ProfileComponent', () => {
6 | let component: ProfileComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(waitForAsync(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ ProfileComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(ProfileComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/register/register.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
2 |
3 | import { RegisterComponent } from './register.component';
4 |
5 | describe('RegisterComponent', () => {
6 | let component: RegisterComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(waitForAsync(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ RegisterComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(RegisterComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/angular-11-social-login/README.md:
--------------------------------------------------------------------------------
1 | # Spring Boot 2 + Angular 10: User Registration and Login using JWT Authentication, OAuth2 Social Login with Facebook, Google, LinkedIn, and Github using Spring Security 5 and Two Factor Authentication (2FA)
2 |
3 | [Creating Backend - Spring REST API - Part 1](https://www.javachinna.com/spring-boot-angular-two-factor-authentication/)
4 |
5 | [Creating Backend - Spring REST API - Part 2](https://www.javachinna.com/2020/10/23/spring-boot-angular-10-user-registration-oauth2-social-login-part-2/)
6 |
7 | [Creating Angular 10 Client Application - Part 3](https://www.javachinna.com/2020/10/28/spring-boot-angular-10-user-registration-oauth2-social-login-part-3/)
8 |
9 | [Secure Spring Boot Angular Application with Two Factor Authentication](https://www.javachinna.com/spring-boot-angular-two-factor-authentication/)
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/board-user/board-user.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
2 |
3 | import { BoardUserComponent } from './board-user.component';
4 |
5 | describe('BoardUserComponent', () => {
6 | let component: BoardUserComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(waitForAsync(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ BoardUserComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(BoardUserComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/register/register.component.css:
--------------------------------------------------------------------------------
1 | label {
2 | display: block;
3 | margin-top: 10px;
4 | }
5 |
6 | .card-container.card {
7 | max-width: 400px !important;
8 | padding: 40px 40px;
9 | }
10 |
11 | .card {
12 | background-color: #f7f7f7;
13 | padding: 20px 25px 30px;
14 | margin: 0 auto 25px;
15 | margin-top: 50px;
16 | -moz-border-radius: 2px;
17 | -webkit-border-radius: 2px;
18 | border-radius: 2px;
19 | -moz-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
20 | -webkit-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
21 | box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
22 | }
23 |
24 | .profile-img-card {
25 | width: 96px;
26 | height: 96px;
27 | margin: 0 auto 10px;
28 | display: block;
29 | -moz-border-radius: 50%;
30 | -webkit-border-radius: 50%;
31 | border-radius: 50%;
32 | }
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/board-admin/board-admin.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
2 |
3 | import { BoardAdminComponent } from './board-admin.component';
4 |
5 | describe('BoardAdminComponent', () => {
6 | let component: BoardAdminComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(waitForAsync(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ BoardAdminComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(BoardAdminComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/common/app.constants.ts:
--------------------------------------------------------------------------------
1 | export class AppConstants {
2 | private static API_BASE_URL = "http://localhost:8080/";
3 | private static OAUTH2_URL = AppConstants.API_BASE_URL + "oauth2/authorization/";
4 | private static REDIRECT_URL = "?redirect_uri=http://localhost:8081/login";
5 | public static API_URL = AppConstants.API_BASE_URL + "api/";
6 | public static AUTH_API = AppConstants.API_URL + "auth/";
7 | public static GOOGLE_AUTH_URL = AppConstants.OAUTH2_URL + "google" + AppConstants.REDIRECT_URL;
8 | public static FACEBOOK_AUTH_URL = AppConstants.OAUTH2_URL + "facebook" + AppConstants.REDIRECT_URL;
9 | public static GITHUB_AUTH_URL = AppConstants.OAUTH2_URL + "github" + AppConstants.REDIRECT_URL;
10 | public static LINKEDIN_AUTH_URL = AppConstants.OAUTH2_URL + "linkedin" + AppConstants.REDIRECT_URL;
11 | }
--------------------------------------------------------------------------------
/angular-11-social-login/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 | # Only exists if Bazel was run
8 | /bazel-out
9 |
10 | # dependencies
11 | /node_modules
12 |
13 | # profiling files
14 | chrome-profiler-events*.json
15 | speed-measure-plugin*.json
16 |
17 | # IDEs and editors
18 | /.idea
19 | .project
20 | .classpath
21 | .c9/
22 | *.launch
23 | .settings/
24 | *.sublime-workspace
25 |
26 | # IDE - VSCode
27 | .vscode/*
28 | !.vscode/settings.json
29 | !.vscode/tasks.json
30 | !.vscode/launch.json
31 | !.vscode/extensions.json
32 | .history/*
33 |
34 | # misc
35 | /.sass-cache
36 | /connect.lock
37 | /coverage
38 | /libpeerconnection.log
39 | npm-debug.log
40 | yarn-error.log
41 | testem.log
42 | /typings
43 |
44 | # System Files
45 | .DS_Store
46 | Thumbs.db
47 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/board-moderator/board-moderator.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
2 |
3 | import { BoardModeratorComponent } from './board-moderator.component';
4 |
5 | describe('BoardModeratorComponent', () => {
6 | let component: BoardModeratorComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(waitForAsync(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ BoardModeratorComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(BoardModeratorComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/spring-boot-oauth2-social-login/src/main/java/com/javachinna/security/oauth2/user/LinkedinOAuth2UserInfo.java:
--------------------------------------------------------------------------------
1 | package com.javachinna.security.oauth2.user;
2 |
3 | import java.util.Map;
4 |
5 | public class LinkedinOAuth2UserInfo extends OAuth2UserInfo {
6 |
7 | public LinkedinOAuth2UserInfo(Map attributes) {
8 | super(attributes);
9 | }
10 |
11 | @Override
12 | public String getId() {
13 | return (String) attributes.get("id");
14 | }
15 |
16 | @Override
17 | public String getName() {
18 | return ((String) attributes.get("localizedFirstName")).concat(" ").concat((String) attributes.get("localizedLastName"));
19 | }
20 |
21 | @Override
22 | public String getEmail() {
23 | return (String) attributes.get("emailAddress");
24 | }
25 |
26 | @Override
27 | public String getImageUrl() {
28 | return (String) attributes.get("pictureUrl");
29 | }
30 | }
--------------------------------------------------------------------------------
/angular-11-social-login/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/dist/zone-testing';
4 | import { getTestBed } from '@angular/core/testing';
5 | import {
6 | BrowserDynamicTestingModule,
7 | platformBrowserDynamicTesting
8 | } from '@angular/platform-browser-dynamic/testing';
9 |
10 | declare const require: {
11 | context(path: string, deep?: boolean, filter?: RegExp): {
12 | keys(): string[];
13 | (id: string): T;
14 | };
15 | };
16 |
17 | // First, initialize the Angular testing environment.
18 | getTestBed().initTestEnvironment(
19 | BrowserDynamicTestingModule,
20 | platformBrowserDynamicTesting()
21 | );
22 | // Then we find all the tests.
23 | const context = require.context('./', true, /\.spec\.ts$/);
24 | // And load the modules.
25 | context.keys().map(context);
26 |
--------------------------------------------------------------------------------
/spring-boot-oauth2-social-login/src/main/java/com/javachinna/exception/ResourceNotFoundException.java:
--------------------------------------------------------------------------------
1 | package com.javachinna.exception;
2 |
3 | import org.springframework.http.HttpStatus;
4 | import org.springframework.web.bind.annotation.ResponseStatus;
5 |
6 | import lombok.Getter;
7 |
8 | @Getter
9 | @ResponseStatus(HttpStatus.NOT_FOUND)
10 | public class ResourceNotFoundException extends RuntimeException {
11 | private static final long serialVersionUID = 7004203416628447047L;
12 | private String resourceName;
13 | private String fieldName;
14 | private Object fieldValue;
15 |
16 | public ResourceNotFoundException(String resourceName, String fieldName, Object fieldValue) {
17 | super(String.format("%s not found with %s : '%s'", resourceName, fieldName, fieldValue));
18 | this.resourceName = resourceName;
19 | this.fieldName = fieldName;
20 | this.fieldValue = fieldValue;
21 | }
22 | }
--------------------------------------------------------------------------------
/spring-boot-oauth2-social-login/src/main/java/com/javachinna/validator/PasswordMatches.java:
--------------------------------------------------------------------------------
1 | package com.javachinna.validator;
2 |
3 | import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
4 | import static java.lang.annotation.ElementType.TYPE;
5 | import static java.lang.annotation.RetentionPolicy.RUNTIME;
6 |
7 | import java.lang.annotation.Documented;
8 | import java.lang.annotation.Retention;
9 | import java.lang.annotation.Target;
10 |
11 | import javax.validation.Constraint;
12 | import javax.validation.Payload;
13 |
14 | @Target({ TYPE, ANNOTATION_TYPE })
15 | @Retention(RUNTIME)
16 | @Constraint(validatedBy = PasswordMatchesValidator.class)
17 | @Documented
18 | public @interface PasswordMatches {
19 |
20 | String message() default "Passwords don't match";
21 |
22 | Class>[] groups() default {};
23 |
24 | Class extends Payload>[] payload() default {};
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/_services/token-storage.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 |
3 | const TOKEN_KEY = 'auth-token';
4 | const USER_KEY = 'auth-user';
5 |
6 | @Injectable({
7 | providedIn: 'root'
8 | })
9 | export class TokenStorageService {
10 |
11 | constructor() { }
12 |
13 | signOut(): void {
14 | window.sessionStorage.clear();
15 | }
16 |
17 | public saveToken(token: string): void {
18 | window.sessionStorage.removeItem(TOKEN_KEY);
19 | window.sessionStorage.setItem(TOKEN_KEY, token);
20 | }
21 |
22 | public getToken(): string {
23 | return sessionStorage.getItem(TOKEN_KEY);
24 | }
25 |
26 | public saveUser(user): void {
27 | window.sessionStorage.removeItem(USER_KEY);
28 | window.sessionStorage.setItem(USER_KEY, JSON.stringify(user));
29 | }
30 |
31 | public getUser(): any {
32 | return JSON.parse(sessionStorage.getItem(USER_KEY));
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/spring-boot-oauth2-social-login/src/main/java/com/javachinna/service/UserService.java:
--------------------------------------------------------------------------------
1 | package com.javachinna.service;
2 |
3 | import java.util.Map;
4 | import java.util.Optional;
5 |
6 | import org.springframework.security.oauth2.core.oidc.OidcIdToken;
7 | import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
8 |
9 | import com.javachinna.dto.LocalUser;
10 | import com.javachinna.dto.SignUpRequest;
11 | import com.javachinna.exception.UserAlreadyExistAuthenticationException;
12 | import com.javachinna.model.User;
13 |
14 | /**
15 | * @author Chinna
16 | * @since 26/3/18
17 | */
18 | public interface UserService {
19 |
20 | public User registerNewUser(SignUpRequest signUpRequest) throws UserAlreadyExistAuthenticationException;
21 |
22 | User findUserByEmail(String email);
23 |
24 | Optional findUserById(Long id);
25 |
26 | LocalUser processUserRegistration(String registrationId, Map attributes, OidcIdToken idToken, OidcUserInfo userInfo);
27 | }
28 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Spring Boot 2 + Angular 10: User Registration and Login using JWT Authentication, OAuth2 Social Login with Facebook, Google, LinkedIn, and Github using Spring Security 5 and Two Factor Authentication (2FA)
2 |
3 | [Creating Backend - Spring REST API - Part 1](https://www.javachinna.com/spring-boot-angular-10-user-registration-oauth2-social-login-part-1/)
4 |
5 | [Creating Backend - Spring REST API - Part 2](https://www.javachinna.com/2020/10/23/spring-boot-angular-10-user-registration-oauth2-social-login-part-2/)
6 |
7 | [Creating Angular 10 Client Application - Part 3](https://www.javachinna.com/2020/10/28/spring-boot-angular-10-user-registration-oauth2-social-login-part-3/)
8 |
9 | [Secure Spring Boot Angular Application with Two Factor Authentication](https://www.javachinna.com/spring-boot-angular-two-factor-authentication/)
10 |
11 | [Dockerize Angular with NGINX and Spring Boot with MySQL using Docker Compose](https://www.javachinna.com/angular-nginx-spring-boot-mysql-docker-compose/)
12 |
--------------------------------------------------------------------------------
/spring-boot-oauth2-social-login/src/main/java/com/javachinna/DemoApplication.java:
--------------------------------------------------------------------------------
1 | package com.javachinna;
2 |
3 | import org.springframework.boot.autoconfigure.SpringBootApplication;
4 | import org.springframework.boot.builder.SpringApplicationBuilder;
5 | import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
6 | import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
7 | import org.springframework.transaction.annotation.EnableTransactionManagement;
8 |
9 | @SpringBootApplication(scanBasePackages = "com.javachinna")
10 | @EnableJpaRepositories
11 | @EnableTransactionManagement
12 | public class DemoApplication extends SpringBootServletInitializer {
13 |
14 | public static void main(String[] args) {
15 | SpringApplicationBuilder app = new SpringApplicationBuilder(DemoApplication.class);
16 | app.run();
17 | }
18 |
19 | @Override
20 | protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
21 | return application.sources(DemoApplication.class);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/angular-11-social-login/e2e/protractor.conf.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | // Protractor configuration file, see link for more information
3 | // https://github.com/angular/protractor/blob/master/lib/config.ts
4 |
5 | const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter');
6 |
7 | /**
8 | * @type { import("protractor").Config }
9 | */
10 | exports.config = {
11 | allScriptsTimeout: 11000,
12 | specs: [
13 | './src/**/*.e2e-spec.ts'
14 | ],
15 | capabilities: {
16 | browserName: 'chrome'
17 | },
18 | directConnect: true,
19 | baseUrl: 'http://localhost:4200/',
20 | framework: 'jasmine',
21 | jasmineNodeOpts: {
22 | showColors: true,
23 | defaultTimeoutInterval: 30000,
24 | print: function() {}
25 | },
26 | onPrepare() {
27 | require('ts-node').register({
28 | project: require('path').join(__dirname, './tsconfig.json')
29 | });
30 | jasmine.getEnv().addReporter(new SpecReporter({
31 | spec: {
32 | displayStacktrace: StacktraceOption.PRETTY
33 | }
34 | }));
35 | }
36 | };
--------------------------------------------------------------------------------
/spring-boot-oauth2-social-login/src/main/java/com/javachinna/config/RestAuthenticationEntryPoint.java:
--------------------------------------------------------------------------------
1 | package com.javachinna.config;
2 |
3 | import java.io.IOException;
4 |
5 | import javax.servlet.ServletException;
6 | import javax.servlet.http.HttpServletRequest;
7 | import javax.servlet.http.HttpServletResponse;
8 |
9 | import org.slf4j.Logger;
10 | import org.slf4j.LoggerFactory;
11 | import org.springframework.security.core.AuthenticationException;
12 | import org.springframework.security.web.AuthenticationEntryPoint;
13 |
14 | public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
15 |
16 | private static final Logger logger = LoggerFactory.getLogger(RestAuthenticationEntryPoint.class);
17 |
18 | @Override
19 | public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
20 | logger.error("Responding with unauthorized error. Message - {}", e.getMessage());
21 | httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getLocalizedMessage());
22 | }
23 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Chinna
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { TokenStorageService } from './_services/token-storage.service';
3 |
4 | @Component({
5 | selector: 'app-root',
6 | templateUrl: './app.component.html',
7 | styleUrls: ['./app.component.css']
8 | })
9 | export class AppComponent implements OnInit {
10 | private roles: string[];
11 | isLoggedIn = false;
12 | showAdminBoard = false;
13 | showModeratorBoard = false;
14 | username: string;
15 |
16 | constructor(private tokenStorageService: TokenStorageService) { }
17 |
18 | ngOnInit(): void {
19 | this.isLoggedIn = !!this.tokenStorageService.getUser();
20 |
21 | if (this.isLoggedIn) {
22 | const user = this.tokenStorageService.getUser();
23 | this.roles = user.roles;
24 |
25 | this.showAdminBoard = this.roles.includes('ROLE_ADMIN');
26 | this.showModeratorBoard = this.roles.includes('ROLE_MODERATOR');
27 |
28 | this.username = user.displayName;
29 | }
30 | }
31 |
32 | logout(): void {
33 | this.tokenStorageService.signOut();
34 | window.location.reload();
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/register/register.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { AuthService } from '../_services/auth.service';
3 |
4 | @Component({
5 | selector: 'app-register',
6 | templateUrl: './register.component.html',
7 | styleUrls: ['./register.component.css']
8 | })
9 | export class RegisterComponent implements OnInit {
10 |
11 | form: any = {};
12 | isSuccessful = false;
13 | isSignUpFailed = false;
14 | isUsing2FA = false;
15 | errorMessage = '';
16 | qrCodeImage = '';
17 |
18 | constructor(private authService: AuthService) { }
19 |
20 | ngOnInit(): void {
21 | }
22 |
23 | onSubmit(): void {
24 | this.authService.register(this.form).subscribe(
25 | data => {
26 | console.log(data);
27 | if(data.using2FA){
28 | this.isUsing2FA = true;
29 | this.qrCodeImage = data.qrCodeImage;
30 | }
31 | this.isSuccessful = true;
32 | this.isSignUpFailed = false;
33 | },
34 | err => {
35 | this.errorMessage = err.error.message;
36 | this.isSignUpFailed = true;
37 | }
38 | );
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/spring-boot-oauth2-social-login/src/main/java/com/javachinna/security/oauth2/user/FacebookOAuth2UserInfo.java:
--------------------------------------------------------------------------------
1 | package com.javachinna.security.oauth2.user;
2 |
3 | import java.util.Map;
4 |
5 | public class FacebookOAuth2UserInfo extends OAuth2UserInfo {
6 | public FacebookOAuth2UserInfo(Map attributes) {
7 | super(attributes);
8 | }
9 |
10 | @Override
11 | public String getId() {
12 | return (String) attributes.get("id");
13 | }
14 |
15 | @Override
16 | public String getName() {
17 | return (String) attributes.get("name");
18 | }
19 |
20 | @Override
21 | public String getEmail() {
22 | return (String) attributes.get("email");
23 | }
24 |
25 | @Override
26 | @SuppressWarnings("unchecked")
27 | public String getImageUrl() {
28 | if (attributes.containsKey("picture")) {
29 | Map pictureObj = (Map) attributes.get("picture");
30 | if (pictureObj.containsKey("data")) {
31 | Map dataObj = (Map) pictureObj.get("data");
32 | if (dataObj.containsKey("url")) {
33 | return (String) dataObj.get("url");
34 | }
35 | }
36 | }
37 | return null;
38 | }
39 | }
--------------------------------------------------------------------------------
/angular-11-social-login/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage-istanbul-reporter'),
13 | require('@angular-devkit/build-angular/plugins/karma')
14 | ],
15 | client: {
16 | clearContext: false // leave Jasmine Spec Runner output visible in browser
17 | },
18 | coverageIstanbulReporter: {
19 | dir: require('path').join(__dirname, './coverage/Angular10SocialLogin'),
20 | reports: ['html', 'lcovonly', 'text-summary'],
21 | fixWebpackSourcePaths: true
22 | },
23 | reporters: ['progress', 'kjhtml'],
24 | port: 9876,
25 | colors: true,
26 | logLevel: config.LOG_INFO,
27 | autoWatch: true,
28 | browsers: ['Chrome'],
29 | singleRun: false,
30 | restartOnFileChange: true
31 | });
32 | };
33 |
--------------------------------------------------------------------------------
/spring-boot-oauth2-social-login/Dockerfile:
--------------------------------------------------------------------------------
1 | #### Stage 1: Build the application
2 | FROM openjdk:11-jdk-slim as build
3 |
4 | # Set the current working directory inside the image
5 | WORKDIR /app
6 |
7 | # Copy maven executable to the image
8 | COPY mvnw .
9 | COPY .mvn .mvn
10 |
11 | # Copy the pom.xml file
12 | COPY pom.xml .
13 |
14 | # Build all the dependencies in preparation to go offline.
15 | # This is a separate step so the dependencies will be cached unless
16 | # the pom.xml file has changed.
17 | RUN ./mvnw dependency:go-offline -B
18 |
19 | # Copy the project source
20 | COPY src src
21 |
22 | # Package the application
23 | RUN ./mvnw package -DskipTests
24 | RUN mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar)
25 |
26 | #### Stage 2: A minimal docker image with command to run the app
27 | FROM openjdk:11-jre-slim
28 |
29 | ARG DEPENDENCY=/app/target/dependency
30 |
31 | # Copy project dependencies from the build stage
32 | COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib
33 | COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF
34 | COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app
35 |
36 | ENTRYPOINT ["java","-cp","app:app/lib/*","com.javachinna.DemoApplication"]
--------------------------------------------------------------------------------
/angular-11-social-login/Dockerfile:
--------------------------------------------------------------------------------
1 | #### Stage 1: Build the angular application
2 | FROM node as build
3 |
4 | # Configure the main working directory inside the docker image.
5 | # This is the base directory used in any further RUN, COPY, and ENTRYPOINT
6 | # commands.
7 | WORKDIR /app
8 |
9 | # Copy the package.json as well as the package-lock.json and install
10 | # the dependencies. This is a separate step so the dependencies
11 | # will be cached unless changes to one of those two files
12 | # are made.
13 | COPY package*.json ./
14 | RUN npm install
15 |
16 | # Copy the main application
17 | COPY . ./
18 |
19 | # Arguments
20 | ARG configuration=production
21 |
22 | # Build the application
23 | RUN npm run build -- --outputPath=./dist/out --configuration $configuration
24 |
25 | #### Stage 2, use the compiled app, ready for production with Nginx
26 | FROM nginx
27 |
28 | # Copy the angular build from Stage 1
29 | COPY --from=build /app/dist/out/ /usr/share/nginx/html
30 |
31 | # Copy our custom nginx config
32 | COPY /nginx-custom.conf /etc/nginx/conf.d/default.conf
33 |
34 |
35 | # Expose port 80 to the Docker host, so we can access it
36 | # from the outside.
37 | EXPOSE 80
38 |
39 | ENTRYPOINT ["nginx","-g","daemon off;"]
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/_services/user.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { HttpClient, HttpHeaders } from '@angular/common/http';
3 | import { Observable } from 'rxjs';
4 | import { AppConstants } from '../common/app.constants';
5 |
6 | const httpOptions = {
7 | headers: new HttpHeaders({ 'Content-Type': 'application/json' })
8 | };
9 |
10 |
11 | @Injectable({
12 | providedIn: 'root'
13 | })
14 | export class UserService {
15 |
16 | constructor(private http: HttpClient) { }
17 |
18 | getPublicContent(): Observable {
19 | return this.http.get(AppConstants.API_URL + 'all', { responseType: 'text' });
20 | }
21 |
22 | getUserBoard(): Observable {
23 | return this.http.get(AppConstants.API_URL + 'user', { responseType: 'text' });
24 | }
25 |
26 | getModeratorBoard(): Observable {
27 | return this.http.get(AppConstants.API_URL + 'mod', { responseType: 'text' });
28 | }
29 |
30 | getAdminBoard(): Observable {
31 | return this.http.get(AppConstants.API_URL + 'admin', { responseType: 'text' });
32 | }
33 |
34 | getCurrentUser(): Observable {
35 | return this.http.get(AppConstants.API_URL + 'user/me', httpOptions);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/totp/totp.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |

4 |
19 |
Welcome {{currentUser.displayName}}
Logged in as {{ currentUser.roles }}.
20 |
21 |
22 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, waitForAsync } from '@angular/core/testing';
2 | import { RouterTestingModule } from '@angular/router/testing';
3 | import { AppComponent } from './app.component';
4 |
5 | describe('AppComponent', () => {
6 | beforeEach(waitForAsync(() => {
7 | TestBed.configureTestingModule({
8 | imports: [
9 | RouterTestingModule
10 | ],
11 | declarations: [
12 | AppComponent
13 | ],
14 | }).compileComponents();
15 | }));
16 |
17 | it('should create the app', () => {
18 | const fixture = TestBed.createComponent(AppComponent);
19 | const app = fixture.componentInstance;
20 | expect(app).toBeTruthy();
21 | });
22 |
23 | it(`should have as title 'Angular10SocialLogin'`, () => {
24 | const fixture = TestBed.createComponent(AppComponent);
25 | const app = fixture.componentInstance;
26 | expect(app.title).toEqual('Angular10SocialLogin');
27 | });
28 |
29 | it('should render title', () => {
30 | const fixture = TestBed.createComponent(AppComponent);
31 | fixture.detectChanges();
32 | const compiled = fixture.nativeElement;
33 | expect(compiled.querySelector('.content span').textContent).toContain('Angular10SocialLogin app is running!');
34 | });
35 | });
36 |
--------------------------------------------------------------------------------
/spring-boot-oauth2-social-login/src/main/java/com/javachinna/security/oauth2/user/OAuth2UserInfoFactory.java:
--------------------------------------------------------------------------------
1 | package com.javachinna.security.oauth2.user;
2 |
3 | import java.util.Map;
4 |
5 | import com.javachinna.dto.SocialProvider;
6 | import com.javachinna.exception.OAuth2AuthenticationProcessingException;
7 |
8 | public class OAuth2UserInfoFactory {
9 | public static OAuth2UserInfo getOAuth2UserInfo(String registrationId, Map attributes) {
10 | if (registrationId.equalsIgnoreCase(SocialProvider.GOOGLE.getProviderType())) {
11 | return new GoogleOAuth2UserInfo(attributes);
12 | } else if (registrationId.equalsIgnoreCase(SocialProvider.FACEBOOK.getProviderType())) {
13 | return new FacebookOAuth2UserInfo(attributes);
14 | } else if (registrationId.equalsIgnoreCase(SocialProvider.GITHUB.getProviderType())) {
15 | return new GithubOAuth2UserInfo(attributes);
16 | } else if (registrationId.equalsIgnoreCase(SocialProvider.LINKEDIN.getProviderType())) {
17 | return new LinkedinOAuth2UserInfo(attributes);
18 | } else if (registrationId.equalsIgnoreCase(SocialProvider.TWITTER.getProviderType())) {
19 | return new GithubOAuth2UserInfo(attributes);
20 | } else {
21 | throw new OAuth2AuthenticationProcessingException("Sorry! Login with " + registrationId + " is not supported yet.");
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/_services/auth.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { HttpClient, HttpHeaders } from '@angular/common/http';
3 | import { Observable } from 'rxjs';
4 | import { AppConstants } from '../common/app.constants';
5 |
6 | const httpOptions = {
7 | headers: new HttpHeaders({ 'Content-Type': 'application/json' })
8 | };
9 |
10 | @Injectable({
11 | providedIn: 'root'
12 | })
13 | export class AuthService {
14 | constructor(private http: HttpClient) { }
15 |
16 | login(credentials): Observable {
17 | return this.http.post(AppConstants.AUTH_API + 'signin', {
18 | email: credentials.username,
19 | password: credentials.password
20 | }, httpOptions);
21 | }
22 |
23 | register(user): Observable {
24 | return this.http.post(AppConstants.AUTH_API + 'signup', {
25 | displayName: user.displayName,
26 | email: user.email,
27 | password: user.password,
28 | matchingPassword: user.matchingPassword,
29 | socialProvider: 'LOCAL',
30 | using2FA: user.using2FA
31 | }, httpOptions);
32 | }
33 |
34 | verify(credentials): Observable {
35 | return this.http.post(AppConstants.AUTH_API + 'verify', credentials.code, {
36 | headers: new HttpHeaders({ 'Content-Type': 'text/plain' })
37 | });
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/app-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { Routes, RouterModule } from '@angular/router';
3 |
4 | import { RegisterComponent } from './register/register.component';
5 | import { LoginComponent } from './login/login.component';
6 | import { HomeComponent } from './home/home.component';
7 | import { ProfileComponent } from './profile/profile.component';
8 | import { BoardUserComponent } from './board-user/board-user.component';
9 | import { BoardModeratorComponent } from './board-moderator/board-moderator.component';
10 | import { BoardAdminComponent } from './board-admin/board-admin.component';
11 | import { TotpComponent } from './totp/totp.component';
12 |
13 | const routes: Routes = [
14 | { path: 'home', component: HomeComponent },
15 | { path: 'login', component: LoginComponent },
16 | { path: 'register', component: RegisterComponent },
17 | { path: 'profile', component: ProfileComponent },
18 | { path: 'user', component: BoardUserComponent },
19 | { path: 'mod', component: BoardModeratorComponent },
20 | { path: 'admin', component: BoardAdminComponent },
21 | { path: 'totp', component: TotpComponent },
22 | { path: '', redirectTo: 'home', pathMatch: 'full' }
23 | ];
24 |
25 | @NgModule({
26 | imports: [RouterModule.forRoot(routes, { relativeLinkResolution: 'legacy' })],
27 | exports: [RouterModule]
28 | })
29 | export class AppRoutingModule { }
30 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/totp/totp.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { AuthService } from '../_services/auth.service';
3 | import { TokenStorageService } from '../_services/token-storage.service';
4 |
5 |
6 | @Component({
7 | selector: 'app-totp',
8 | templateUrl: './totp.component.html',
9 | styleUrls: ['./totp.component.css']
10 | })
11 | export class TotpComponent implements OnInit {
12 |
13 | form: any = {};
14 | isLoggedIn = false;
15 | isLoginFailed = false;
16 | errorMessage = '';
17 | currentUser: any;
18 |
19 | constructor(private authService: AuthService, private tokenStorage: TokenStorageService) {}
20 |
21 | ngOnInit(): void {
22 | if (this.tokenStorage.getUser()) {
23 | this.isLoggedIn = true;
24 | this.currentUser = this.tokenStorage.getUser();
25 | }
26 | }
27 |
28 | onSubmit(): void {
29 | this.authService.verify(this.form).subscribe(
30 | data => {
31 | this.tokenStorage.saveToken(data.accessToken);
32 | this.login(data.user);
33 | },
34 | err => {
35 | this.errorMessage = err.error.message;
36 | this.isLoginFailed = true;
37 | }
38 | );
39 | }
40 |
41 | login(user): void {
42 | this.tokenStorage.saveUser(user);
43 | this.isLoginFailed = false;
44 | this.isLoggedIn = true;
45 | this.currentUser = this.tokenStorage.getUser();
46 | window.location.reload();
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/angular-11-social-login/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular10-jwt-auth",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "ng": "ng",
6 | "start": "ng serve",
7 | "build": "ng build",
8 | "test": "ng test",
9 | "lint": "ng lint",
10 | "e2e": "ng e2e"
11 | },
12 | "private": true,
13 | "dependencies": {
14 | "@angular/animations": "~11.0.2",
15 | "@angular/common": "~11.0.2",
16 | "@angular/compiler": "~11.0.2",
17 | "@angular/core": "~11.0.2",
18 | "@angular/forms": "~11.0.2",
19 | "@angular/platform-browser": "~11.0.2",
20 | "@angular/platform-browser-dynamic": "~11.0.2",
21 | "@angular/router": "~11.0.2",
22 | "rxjs": "~6.5.5",
23 | "tslib": "^2.0.0",
24 | "zone.js": "~0.10.3"
25 | },
26 | "devDependencies": {
27 | "@angular-devkit/build-angular": "^0.1100.2",
28 | "@angular/cli": "~11.0.2",
29 | "@angular/compiler-cli": "~11.0.2",
30 | "@types/jasmine": "~3.6.0",
31 | "@types/jasminewd2": "~2.0.3",
32 | "@types/node": "^12.11.1",
33 | "codelyzer": "^6.0.0",
34 | "jasmine-core": "~3.6.0",
35 | "jasmine-spec-reporter": "~5.0.0",
36 | "karma": "~5.1.1",
37 | "karma-chrome-launcher": "~3.1.0",
38 | "karma-coverage-istanbul-reporter": "~3.0.2",
39 | "karma-jasmine": "~4.0.0",
40 | "karma-jasmine-html-reporter": "^1.5.0",
41 | "protractor": "~7.0.0",
42 | "ts-node": "~8.3.0",
43 | "tslint": "~6.1.0",
44 | "typescript": "~4.0.5"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/_helpers/auth.interceptor.ts:
--------------------------------------------------------------------------------
1 | import { HTTP_INTERCEPTORS, HttpEvent } from '@angular/common/http';
2 | import { Injectable } from '@angular/core';
3 | import { HttpInterceptor, HttpHandler, HttpRequest, HttpErrorResponse } from '@angular/common/http';
4 | import { Router } from '@angular/router';
5 | import { TokenStorageService } from '../_services/token-storage.service';
6 | import {tap} from 'rxjs/operators';
7 | import { Observable } from 'rxjs';
8 |
9 | const TOKEN_HEADER_KEY = 'Authorization';
10 |
11 | @Injectable()
12 | export class AuthInterceptor implements HttpInterceptor {
13 | constructor(private token: TokenStorageService, private router: Router) {
14 |
15 | }
16 |
17 | intercept(req: HttpRequest, next: HttpHandler): Observable> {
18 | let authReq = req;
19 | const loginPath = '/login';
20 | const token = this.token.getToken();
21 | if (token != null) {
22 | authReq = req.clone({ headers: req.headers.set(TOKEN_HEADER_KEY, 'Bearer ' + token) });
23 | }
24 | return next.handle(authReq).pipe( tap(() => {},
25 | (err: any) => {
26 | if (err instanceof HttpErrorResponse) {
27 | if (err.status !== 401 || window.location.pathname === loginPath) {
28 | return;
29 | }
30 | this.token.signOut();
31 | window.location.href = loginPath;
32 | }
33 | }
34 | ));
35 | }
36 | }
37 |
38 | export const authInterceptorProviders = [
39 | { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }
40 | ];
41 |
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { BrowserModule } from '@angular/platform-browser';
2 | import { NgModule } from '@angular/core';
3 |
4 | import { AppRoutingModule } from './app-routing.module';
5 | import { FormsModule } from '@angular/forms';
6 | import { HttpClientModule } from '@angular/common/http';
7 |
8 | import { AppComponent } from './app.component';
9 | import { LoginComponent } from './login/login.component';
10 | import { RegisterComponent } from './register/register.component';
11 | import { HomeComponent } from './home/home.component';
12 | import { ProfileComponent } from './profile/profile.component';
13 | import { BoardAdminComponent } from './board-admin/board-admin.component';
14 | import { BoardModeratorComponent } from './board-moderator/board-moderator.component';
15 | import { BoardUserComponent } from './board-user/board-user.component';
16 | import { TotpComponent } from './totp/totp.component';
17 |
18 | import { authInterceptorProviders } from './_helpers/auth.interceptor';
19 |
20 | @NgModule({
21 | declarations: [
22 | AppComponent,
23 | LoginComponent,
24 | RegisterComponent,
25 | HomeComponent,
26 | ProfileComponent,
27 | BoardAdminComponent,
28 | BoardModeratorComponent,
29 | BoardUserComponent,
30 | TotpComponent
31 | ],
32 | imports: [
33 | BrowserModule,
34 | AppRoutingModule,
35 | FormsModule,
36 | HttpClientModule
37 | ],
38 | providers: [authInterceptorProviders],
39 | bootstrap: [AppComponent]
40 | })
41 | export class AppModule { }
42 |
--------------------------------------------------------------------------------
/spring-boot-oauth2-social-login/src/main/java/com/javachinna/config/AppProperties.java:
--------------------------------------------------------------------------------
1 | package com.javachinna.config;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | import org.springframework.boot.context.properties.ConfigurationProperties;
7 | import org.springframework.stereotype.Component;
8 |
9 | @Component
10 | @ConfigurationProperties(prefix = "app")
11 | public class AppProperties {
12 | private final Auth auth = new Auth();
13 | private final OAuth2 oauth2 = new OAuth2();
14 |
15 | public static class Auth {
16 | private String tokenSecret;
17 | private long tokenExpirationMsec;
18 |
19 | public String getTokenSecret() {
20 | return tokenSecret;
21 | }
22 |
23 | public void setTokenSecret(String tokenSecret) {
24 | this.tokenSecret = tokenSecret;
25 | }
26 |
27 | public long getTokenExpirationMsec() {
28 | return tokenExpirationMsec;
29 | }
30 |
31 | public void setTokenExpirationMsec(long tokenExpirationMsec) {
32 | this.tokenExpirationMsec = tokenExpirationMsec;
33 | }
34 | }
35 |
36 | public static final class OAuth2 {
37 | private List authorizedRedirectUris = new ArrayList<>();
38 |
39 | public List getAuthorizedRedirectUris() {
40 | return authorizedRedirectUris;
41 | }
42 |
43 | public OAuth2 authorizedRedirectUris(List authorizedRedirectUris) {
44 | this.authorizedRedirectUris = authorizedRedirectUris;
45 | return this;
46 | }
47 | }
48 |
49 | public Auth getAuth() {
50 | return auth;
51 | }
52 |
53 | public OAuth2 getOauth2() {
54 | return oauth2;
55 | }
56 | }
--------------------------------------------------------------------------------
/spring-boot-oauth2-social-login/src/main/java/com/javachinna/util/GeneralUtils.java:
--------------------------------------------------------------------------------
1 | package com.javachinna.util;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 | import java.util.Set;
6 | import java.util.stream.Collectors;
7 |
8 | import org.springframework.security.core.authority.SimpleGrantedAuthority;
9 |
10 | import com.javachinna.dto.LocalUser;
11 | import com.javachinna.dto.SocialProvider;
12 | import com.javachinna.dto.UserInfo;
13 | import com.javachinna.model.Role;
14 | import com.javachinna.model.User;
15 |
16 | /**
17 | *
18 | * @author Chinna
19 | *
20 | */
21 | public class GeneralUtils {
22 |
23 | public static List buildSimpleGrantedAuthorities(final Set roles) {
24 | List authorities = new ArrayList<>();
25 | for (Role role : roles) {
26 | authorities.add(new SimpleGrantedAuthority(role.getName()));
27 | }
28 | return authorities;
29 | }
30 |
31 | public static SocialProvider toSocialProvider(String providerId) {
32 | for (SocialProvider socialProvider : SocialProvider.values()) {
33 | if (socialProvider.getProviderType().equals(providerId)) {
34 | return socialProvider;
35 | }
36 | }
37 | return SocialProvider.LOCAL;
38 | }
39 |
40 | public static UserInfo buildUserInfo(LocalUser localUser) {
41 | List roles = localUser.getAuthorities().stream().map(item -> item.getAuthority()).collect(Collectors.toList());
42 | User user = localUser.getUser();
43 | return new UserInfo(user.getId().toString(), user.getDisplayName(), user.getEmail(), roles);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/spring-boot-oauth2-social-login/src/main/java/com/javachinna/controller/UserController.java:
--------------------------------------------------------------------------------
1 | package com.javachinna.controller;
2 |
3 | import org.springframework.http.ResponseEntity;
4 | import org.springframework.security.access.prepost.PreAuthorize;
5 | import org.springframework.web.bind.annotation.GetMapping;
6 | import org.springframework.web.bind.annotation.RequestMapping;
7 | import org.springframework.web.bind.annotation.RestController;
8 |
9 | import com.javachinna.config.CurrentUser;
10 | import com.javachinna.dto.LocalUser;
11 | import com.javachinna.util.GeneralUtils;
12 |
13 | @RestController
14 | @RequestMapping("/api")
15 | public class UserController {
16 |
17 | @GetMapping("/user/me")
18 | @PreAuthorize("hasRole('USER')")
19 | public ResponseEntity> getCurrentUser(@CurrentUser LocalUser user) {
20 | return ResponseEntity.ok(GeneralUtils.buildUserInfo(user));
21 | }
22 |
23 | @GetMapping("/all")
24 | public ResponseEntity> getContent() {
25 | return ResponseEntity.ok("Public content goes here");
26 | }
27 |
28 | @GetMapping("/user")
29 | @PreAuthorize("hasRole('USER')")
30 | public ResponseEntity> getUserContent() {
31 | return ResponseEntity.ok("User content goes here");
32 | }
33 |
34 | @GetMapping("/admin")
35 | @PreAuthorize("hasRole('ADMIN')")
36 | public ResponseEntity> getAdminContent() {
37 | return ResponseEntity.ok("Admin content goes here");
38 | }
39 |
40 | @GetMapping("/mod")
41 | @PreAuthorize("hasRole('MODERATOR')")
42 | public ResponseEntity> getModeratorContent() {
43 | return ResponseEntity.ok("Moderator content goes here");
44 | }
45 | }
--------------------------------------------------------------------------------
/spring-boot-oauth2-social-login/src/main/java/com/javachinna/security/oauth2/CustomOidcUserService.java:
--------------------------------------------------------------------------------
1 | package com.javachinna.security.oauth2;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.security.core.AuthenticationException;
5 | import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
6 | import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
7 | import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
8 | import org.springframework.security.oauth2.core.oidc.user.OidcUser;
9 | import org.springframework.stereotype.Service;
10 |
11 | import com.javachinna.exception.OAuth2AuthenticationProcessingException;
12 | import com.javachinna.service.UserService;
13 |
14 | @Service
15 | public class CustomOidcUserService extends OidcUserService {
16 |
17 | @Autowired
18 | private UserService userService;
19 |
20 | @Override
21 | public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
22 | OidcUser oidcUser = super.loadUser(userRequest);
23 | try {
24 | return userService.processUserRegistration(userRequest.getClientRegistration().getRegistrationId(), oidcUser.getAttributes(), oidcUser.getIdToken(),
25 | oidcUser.getUserInfo());
26 | } catch (AuthenticationException ex) {
27 | throw ex;
28 | } catch (Exception ex) {
29 | ex.printStackTrace();
30 | // Throwing an instance of AuthenticationException will trigger the
31 | // OAuth2AuthenticationFailureHandler
32 | throw new OAuth2AuthenticationProcessingException(ex.getMessage(), ex.getCause());
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/angular-11-social-login/src/app/login/login.component.css:
--------------------------------------------------------------------------------
1 | label {
2 | display: block;
3 | margin-top: 10px;
4 | }
5 |
6 | .card-container.card {
7 | max-width: 400px !important;
8 | padding: 40px 40px;
9 | }
10 |
11 | .card {
12 | background-color: #f7f7f7;
13 | padding: 20px 25px 30px;
14 | margin: 0 auto 25px;
15 | margin-top: 50px;
16 | -moz-border-radius: 2px;
17 | -webkit-border-radius: 2px;
18 | border-radius: 2px;
19 | -moz-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
20 | -webkit-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
21 | box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
22 | }
23 |
24 | .profile-img-card {
25 | width: 96px;
26 | height: 96px;
27 | margin: 0 auto 10px;
28 | display: block;
29 | -moz-border-radius: 50%;
30 | -webkit-border-radius: 50%;
31 | border-radius: 50%;
32 | }
33 |
34 | .content-divider.center {
35 | text-align: center;
36 | }
37 |
38 | .content-divider {
39 | position: relative;
40 | z-index: 1;
41 | }
42 |
43 | .content-divider>span, .content-divider>a {
44 | background-color: #fff;
45 | color: #000;
46 | display: inline-block;
47 | padding: 2px 12px;
48 | border-radius: 3px;
49 | text-transform: uppercase;
50 | font-weight: 500;
51 | }
52 |
53 | .content-divider>span:before, .content-divider>a:before {
54 | content: "";
55 | position: absolute;
56 | top: 50%;
57 | left: 0;
58 | height: 1px;
59 | background-color: #ddd;
60 | width: 100%;
61 | z-index: -1;
62 | }
63 |
64 | .social-login .btn-img {
65 | width: 30px;
66 | height: auto;
67 | padding: 0.6rem 0;
68 | margin-right: 15px;
69 | }
70 |
71 | .btn-img-linkedin {
72 | width: auto;
73 | height: 50px;
74 | padding: 0.6rem 0;
75 | margin-right: 10px;
76 | }
77 |
78 | .form-group .has-error {
79 | color: red;
80 | }
--------------------------------------------------------------------------------
/spring-boot-oauth2-social-login/src/main/java/com/javachinna/exception/handler/RestResponseEntityExceptionHandler.java:
--------------------------------------------------------------------------------
1 | package com.javachinna.exception.handler;
2 |
3 | import java.util.stream.Collectors;
4 |
5 | import org.springframework.http.HttpHeaders;
6 | import org.springframework.http.HttpStatus;
7 | import org.springframework.http.ResponseEntity;
8 | import org.springframework.validation.BindingResult;
9 | import org.springframework.validation.FieldError;
10 | import org.springframework.web.bind.MethodArgumentNotValidException;
11 | import org.springframework.web.bind.annotation.ControllerAdvice;
12 | import org.springframework.web.context.request.WebRequest;
13 | import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
14 |
15 | import com.javachinna.dto.ApiResponse;
16 |
17 | @ControllerAdvice
18 | public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
19 |
20 | public RestResponseEntityExceptionHandler() {
21 | super();
22 | }
23 |
24 | @Override
25 | protected ResponseEntity