1. 概要

Auth0 は、ネイティブ、シングルページアプリケーション、Webなどのさまざまなタイプのアプリケーションに認証および承認サービスを提供します。 さらに、シングルサインオン、ソーシャルログイン、多要素認証などのさまざまな機能を実装できます。

このチュートリアルでは、Auth0アカウントの主要な構成とともに、ステップバイステップガイドを通じてSpringAuth0を使用したセキュリティについて説明します。

2. Auth0の設定

2.1. Auth0サインアップ

まず、 無制限のログインで最大7,000人のアクティブユーザーにアクセスを提供する無料のAuth0プランにサインアップします。 ただし、すでに1つある場合は、このセクションをスキップできます。

2.2. ダッシュボード

Auth0アカウントにログインすると、ログインアクティビティ、最新のログイン、新規サインアップなどの詳細を強調表示するダッシュボードが表示されます。

2.3. 新しいアプリケーションを作成する

次に、[アプリケーション]メニューから、Spring Boot用の新しいOpenID Connect(OIDC)アプリケーションを作成します。

さらに、 Native Single-Page Apps Machine to Machine Appsなどの利用可能なオプションから、アプリケーションタイプとして通常のWebアプリケーションを選択します

2.4. アプリケーションの設定

次に、アプリケーションを指すコールバックURLログアウトURLなどのいくつかのアプリケーションURIを構成します。

2.5. クライアントの資格情報

最後に、アプリに関連付けられているドメインクライアントID、クライアントシークレットの値を取得します。

これらのクレデンシャルは、Spring Boot AppのAuth0セットアップに必要なため、手元に置いておいてください。

3. Spring Boot App Setup

Auth0アカウントでキー構成の準備ができたので、Auth0セキュリティをSpring Bootアプリに統合する準備が整いました。

3.1. Maven

まず、最新の mvc-auth-commonsMaven依存関係をpom.xmlに追加しましょう。

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>mvc-auth-commons</artifactId>
    <version>1.2.0</version>
</dependency>

3.2. Gradle

同様に、Gradleを使用する場合、mvc-auth-commons依存関係をbuild.gradleファイルに追加できます。

compile 'com.auth0:mvc-auth-commons:1.2.0'

3.3. application.properties

Spring Bootアプリでは、Auth0アカウントの認証を有効にするために、クライアントIDクライアントシークレットなどの情報が必要です。 そこで、それらをapplication.propertiesファイルに追加します。

com.auth0.domain: dev-example.auth0.com
com.auth0.clientId: {clientId}
com.auth0.clientSecret: {clientSecret}

3.4. AuthConfig

次に、 AuthConfig クラスを作成して、application.propertiesファイルからAuth0プロパティを読み取ります。

@Configuration
@EnableWebSecurity
public class AuthConfig extends WebSecurityConfigurerAdapter {
    @Value(value = "${com.auth0.domain}")
    private String domain;

    @Value(value = "${com.auth0.clientId}")
    private String clientId;

    @Value(value = "${com.auth0.clientSecret}")
    private String clientSecret;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();
        http
          .authorizeRequests()
          .antMatchers("/callback", "/login", "/").permitAll()
          .anyRequest().authenticated()
          .and()
          .formLogin()
          .loginPage("/login")
          .and()
          .logout().logoutSuccessHandler(logoutSuccessHandler()).permitAll();
    }
}

さらに、 AuthConfig クラスは、WebSecurityConfigurerAdapterクラスを拡張することによってWebセキュリティを有効にするように構成されています。

3.5. AuthenticationController

最後に、AuthenticationControllerクラスのbean参照をすでに説明したAuthConfigクラスに追加します。

@Bean
public AuthenticationController authenticationController() throws UnsupportedEncodingException {
    JwkProvider jwkProvider = new JwkProviderBuilder(domain).build();
    return AuthenticationController.newBuilder(domain, clientId, clientSecret)
      .withJwkProvider(jwkProvider)
      .build();
}

ここでは、 AuthenticationController クラスのインスタンスを構築する際に、JwkProviderBuilderクラスを使用しました。 これを使用して公開鍵をフェッチし、トークンの署名を検証します(デフォルトでは、トークンはRS256非対称署名アルゴリズムを使用して署名されます)。

さらに、 authenticationController Beanは、ログイン用の認証URLを提供し、コールバック要求を処理します。

4. AuthController

次に、ログインおよびコールバック機能用のAuthControllerクラスを作成します。

@Controller
public class AuthController {
    @Autowired
    private AuthConfig config;

    @Autowired 
    private AuthenticationController authenticationController;
}

ここでは、前のセクションで説明したAuthConfigクラスとAuthenticationControllerクラスの依存関係を挿入しました。

4.1. ログイン

login メソッドを作成して、Spring Bootアプリがユーザーを認証できるようにします。

@GetMapping(value = "/login")
protected void login(HttpServletRequest request, HttpServletResponse response) {
    String redirectUri = "http://localhost:8080/callback";
    String authorizeUrl = authenticationController.buildAuthorizeUrl(request, response, redirectUri)
      .withScope("openid email")
      .build();
    response.sendRedirect(authorizeUrl);
}

buildAuthorizeUrl メソッドは、Auth0承認URLを生成し、デフォルトのAuth0サインイン画面にリダイレクトします。

4.2. 折り返し電話

ユーザーがAuth0クレデンシャルでサインインすると、コールバックリクエストがSpringBootAppに送信されます。 そのために、コールバックメソッドを作成しましょう。

@GetMapping(value="/callback")
public void callback(HttpServletRequest request, HttpServletResponse response) {
    Tokens tokens = authenticationController.handle(request, response);
    
    DecodedJWT jwt = JWT.decode(tokens.getIdToken());
    TestingAuthenticationToken authToken2 = new TestingAuthenticationToken(jwt.getSubject(),
      jwt.getToken());
    authToken2.setAuthenticated(true);
    
    SecurityContextHolder.getContext().setAuthentication(authToken2);
    response.sendRedirect(config.getContextPath(request) + "/"); 
}

コールバックリクエストを処理して、認証の成功を表すaccessTokenidTokenを取得しました。 次に、 TestingAuthenticationToken オブジェクトを作成して、SecurityContextHolderで認証を設定しました。

ただし、使いやすさを向上させるために、AbstractAuthenticationTokenクラスの実装を作成できます。

5. HomeController

最後に、アプリケーションのランディングページのデフォルトのマッピングを使用してHomeControllerを作成します。

@Controller
public class HomeController {
    @GetMapping(value = "/")
    @ResponseBody
    public String home(final Authentication authentication) {
        TestingAuthenticationToken token = (TestingAuthenticationToken) authentication;
        DecodedJWT jwt = JWT.decode(token.getCredentials().toString());
        String email = jwt.getClaims().get("email").asString();
        return "Welcome, " + email + "!";
    }
}

ここでは、idTokenからDecodedJWTオブジェクトを抽出しました。 さらに、電子メールなどのユーザー情報はクレームから取得されます。

それでおしまい! Spring Boot Appは、Auth0セキュリティサポートを備えています。 Mavenコマンドを使用してアプリを実行してみましょう。

mvn spring-boot:run

localhost:8080 / login、でアプリケーションにアクセスすると、Auth0によって提供されるデフォルトのサインインページが表示されます。

登録ユーザーの資格情報を使用してログインすると、ユーザーの電子メールを含むウェルカムメッセージが表示されます。

また、自己登録用のデフォルトのサインイン画面に「サインアップ」ボタン(「ログイン」の横)があります。

6. サインアップ

6.1. 自己登録

初めて、「サインアップ」ボタンを使用してAuth0アカウントを作成し、電子メールやパスワードなどの情報を提供できるようになりました。

6.2. ユーザーを作成する

または、Auth0アカウントのUsersメニューから新しいユーザーを作成できます。

6.3. 接続設定

さらに、Spring Bootアプリへのサインアップ/サインインのために、データベースやソーシャルログインなどのさまざまなタイプの接続を選択できます。

さらに、さまざまなソーシャルコネクションを選択できます。

7. LogoutController

ログイン機能とコールバック機能を確認したので、ログアウト機能をSpring Bootアプリに追加できます。

LogoutSuccessHandlerクラスを実装するLogoutControllerクラスを作成しましょう。

@Controller
public class LogoutController implements LogoutSuccessHandler {
    @Autowired
    private AuthConfig config;

    @Override
    public void onLogoutSuccess(HttpServletRequest req, HttpServletResponse res, 
      Authentication authentication) {
        if (req.getSession() != null) {
            req.getSession().invalidate();
        }
        String returnTo = "http://localhost:8080/";
        String logoutUrl = "https://dev-example.auth0.com/v2/logout?client_id=" +
          config.getClientId() + "&returnTo=" +returnTo;
        res.sendRedirect(logoutUrl);
    }
}

ここで、 onLogoutSuccess メソッドは、 / v2 / logoutAuth0ログアウトURLを呼び出すためにオーバーライドされます。

8. Auth0管理API

これまで、Spring BootアプリでのAuth0セキュリティの統合を見てきました。 それでは、同じアプリでAuth0 Management API(システムAPI)を操作してみましょう。

8.1. 新しいアプリケーションを作成する

まず、Auth0 Management APIにアクセスするために、Auth0アカウントに Machine to MachineApplicationを作成します。

8.2. 承認

次に、ユーザーの読み取り/作成の権限を持つAuth0ManagementAPIに承認を追加します。

8.3. クライアントの資格情報

最後に、クライアントIDクライアントシークレットを受け取り、Spring BootアプリからAuth0管理アプリにアクセスします。

8.4. アクセストークン

前のセクションで受け取ったクライアント資格情報を使用して、Auth0管理アプリのアクセストークンを生成しましょう。

public String getManagementApiToken() {
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);

    JSONObject requestBody = new JSONObject();
    requestBody.put("client_id", "auth0ManagementAppClientId");
    requestBody.put("client_secret", "auth0ManagementAppClientSecret");
    requestBody.put("audience", "https://dev-example.auth0.com/api/v2/");
    requestBody.put("grant_type", "client_credentials"); 

    HttpEntity<String> request = new HttpEntity<String>(requestBody.toString(), headers);

    RestTemplate restTemplate = new RestTemplate();
    HashMap<String, String> result = restTemplate
      .postForObject("https://dev-example.auth0.com/oauth/token", request, HashMap.class);

    return result.get("access_token");
}

ここでは、 / oauth / token Auth0トークンURLにRESTリクエストを送信して、アクセストークンと更新トークンを取得しました。

また、これらのクライアント資格情報を application.properties ファイルに保存し、AuthConfigクラスを使用して読み取ることができます。

8.5. UserController

その後、usersメソッドを使用してUserControllerクラスを作成しましょう。

@Controller
public class UserController {
    @GetMapping(value="/users")
    @ResponseBody
    public ResponseEntity<String> users(HttpServletRequest request, HttpServletResponse response) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.set("Authorization", "Bearer " + getManagementApiToken());
        
        HttpEntity<String> entity = new HttpEntity<String>(headers);
        
        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<String> result = restTemplate
          .exchange("https://dev-example.auth0.com/api/v2/users", HttpMethod.GET, entity, String.class);
        return result;
    }
}

users メソッドは、前のセクションで生成されたアクセストークンを使用して / api / v2 / users Auth0 APIにGETリクエストを行うことにより、すべてのユーザーのリストをフェッチします。

それでは、 localhost:8080 / users にアクセスして、すべてのユーザーを含むJSON応答を受信しましょう。

[{
    "created_at": "2020-05-05T14:38:18.955Z",
    "email": "[email protected]",
    "email_verified": true,
    "identities": [
        {
            "user_id": "5eb17a5a1cc1ac0c1487c37f78758",
            "provider": "auth0",
            "connection": "Username-Password-Authentication",
            "isSocial": false
        }
    ],
    "name": "[email protected]",
    "nickname": "ansh",
    "logins_count": 64
    // ...
}]

8.6. ユーザーを作成

同様に、 / api / v2 / users Auth0 APIに対してPOSTリクエストを行うことで、ユーザーを作成できます。

@GetMapping(value = "/createUser")
@ResponseBody
public ResponseEntity<String> createUser(HttpServletResponse response) {
    JSONObject request = new JSONObject();
    request.put("email", "[email protected]");
    request.put("given_name", "Norman");
    request.put("family_name", "Lewis");
    request.put("connection", "Username-Password-Authentication");
    request.put("password", "Pa33w0rd");
    
    // ...
    ResponseEntity<String> result = restTemplate
      .postForEntity("https://dev-example.auth0.com/api/v2/users", request.toString(), String.class);
    return result;
}

次に、 localhost:8080 / createUser にアクセスして、新しいユーザーの詳細を確認しましょう。

{
    "created_at": "2020-05-10T12:30:15.343Z",
    "email": "[email protected]",
    "email_verified": false,
    "family_name": "Lewis",
    "given_name": "Norman",
    "identities": [
        {
            "connection": "Username-Password-Authentication",
            "user_id": "5eb7f3d76b69bc0c120a8901576",
            "provider": "auth0",
            "isSocial": false
        }
    ],
    "name": "[email protected]",
    "nickname": "norman.lewis",
    // ...
}

同様に、権限に応じて、Auth0 APIを使用して、すべての接続の一覧表示、接続の作成、すべてのクライアントの一覧表示、クライアントの作成などのさまざまな操作を実行できます。

9. 結論

このチュートリアルでは、Auth0を使用したSpringSecurityについて説明しました。

まず、基本的な構成でAuth0アカウントを設定します。 次に、Spring Boot Appを作成し、application.propertiesをAuth0とのSpringSecurity統合用に構成しました。

次に、Auth0管理APIのAPIトークンの作成を検討しました。 最後に、すべてのユーザーの取得やユーザーの作成などの機能について検討しました。

いつものように、すべてのコード実装はGitHub利用できます。