Okta Java SDK

認証ライブラリOktaJavaOAuth2OIDCIdentity EngineSDK

認証ライブラリ

Okta Java SDK

概要

Okta Java SDKは、Javaアプリケーション向けの公式認証ライブラリです。Okta Identity EngineとClassic Engineの両方をサポートし、OAuth2、OpenID Connect、SAMLによる認証機能を提供します。管理SDK(okta-sdk-java)と認証SDK(okta-auth-java)の2つの主要コンポーネントを持ち、サーバーサイドコードでOktaの管理APIとの連携や、詳細な認証ワークフローの実装が可能です。

詳細

Okta Java SDKは、2024年7月に最新版がリリースされた活発に開発されているライブラリです。主要な特徴として、Identity Engine SDKによる最新のセキュリティ機能のサポート、Spring Bootとの統合機能、状態マシンベースの認証フロー管理があります。

2つの主要SDKには明確な役割分担があります。Management SDK(okta-sdk-java)は、Okta管理APIとの連携に使用し、ユーザー、グループ、アプリケーションの管理を行います。一方、Authentication SDK(okta-auth-java)は、認証ワークフローの詳細な制御を提供しますが、より多くの責任と保守作業が必要となります。多くのアプリケーションでは、OktaのホストサインインページまたはSign-in Widgetの使用が推奨されています。

Identity Engineへのアップグレード後は、既存のClassic Engine Javaライブラリと併用しながら、最新のIdentity Engine Java SDKパッケージを追加することが推奨されています。

メリット・デメリット

メリット

  • 公式サポート: Oktaが公式に提供・保守する信頼性の高いライブラリ
  • 包括的な機能: OAuth2、OIDC、SAMLなど主要な認証プロトコルをサポート
  • Identity Engine対応: 最新のOktaプラットフォーム機能に対応
  • Spring Boot統合: Spring Bootアプリケーションとのシームレスな統合
  • 柔軟な実装: 管理機能と認証機能の両方を同一環境で利用可能
  • アクティブな開発: 2024年も継続的にアップデートされている

デメリット

  • 複雑な設定: 認証SDKを使用する場合の状態管理が複雑
  • 学習コストの高さ: 状態マシンの概念と各状態のハンドラー実装が必要
  • 保守負担: 詳細な認証ワークフローを実装する場合の保守コストが高い
  • 依存関係: 多数の外部ライブラリへの依存

参考ページ

書き方の例

Maven依存関係の設定

<!-- Okta Management SDK -->
<dependency>
    <groupId>com.okta.sdk</groupId>
    <artifactId>okta-sdk-api</artifactId>
    <version>19.0.0</version>
</dependency>
<dependency>
    <groupId>com.okta.sdk</groupId>
    <artifactId>okta-sdk-impl</artifactId>
    <version>19.0.0</version>
    <scope>runtime</scope>
</dependency>

<!-- Spring Boot統合(推奨) -->
<dependency>
    <groupId>com.okta.spring</groupId>
    <artifactId>okta-spring-sdk</artifactId>
    <version>3.0.7</version>
</dependency>

<!-- Authentication SDK(高度な制御が必要な場合) -->
<dependency>
    <groupId>com.okta.authn.sdk</groupId>
    <artifactId>okta-authn-sdk-api</artifactId>
    <version>2.0.1</version>
</dependency>
<dependency>
    <groupId>com.okta.authn.sdk</groupId>
    <artifactId>okta-authn-sdk-impl</artifactId>
    <version>2.0.1</version>
    <scope>runtime</scope>
</dependency>

基本的なManagement SDK使用例

import com.okta.sdk.client.Client;
import com.okta.sdk.client.Clients;
import com.okta.sdk.resource.user.User;
import com.okta.sdk.resource.user.UserBuilder;
import com.okta.sdk.resource.user.UserList;

public class OktaManagementExample {
    public static void main(String[] args) {
        // Oktaクライアントの初期化
        Client client = Clients.builder()
            .setOrgUrl("https://your-org.okta.com")
            .setClientId("your-client-id")
            .setPrivateKey("path/to/private-key.pem")
            .build();

        // ユーザー一覧の取得
        UserList users = client.listUsers();
        users.forEach(user -> {
            System.out.println("User: " + user.getProfile().getLogin());
        });

        // 新しいユーザーの作成
        User user = UserBuilder.instance()
            .setEmail("[email protected]")
            .setFirstName("New")
            .setLastName("User")
            .setLogin("[email protected]")
            .setPassword("TempPassword123!".toCharArray())
            .setActive(true)
            .buildAndCreate(client);

        System.out.println("Created user: " + user.getId());
    }
}

Spring Boot設定例

// application.properties
/*
okta.oauth2.issuer=https://your-org.okta.com/oauth2/default
okta.oauth2.client-id=your-client-id
okta.oauth2.client-secret=your-client-secret
okta.oauth2.redirect-uri=/authorization-code/callback
okta.oauth2.post-logout-redirect-uri=http://localhost:8080/
*/

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(authz -> authz
                .requestMatchers("/", "/home").permitAll()
                .anyRequest().authenticated()
            )
            .oauth2Login(oauth2 -> oauth2
                .defaultSuccessUrl("/dashboard", true)
            )
            .oauth2ResourceServer(oauth2 -> oauth2
                .jwt(jwt -> jwt.decoder(jwtDecoder()))
            );
        return http.build();
    }

    @Bean
    public JwtDecoder jwtDecoder() {
        return OktaJwtDecoder.create(
            "https://your-org.okta.com/oauth2/default"
        );
    }
}

Authentication SDKを使った詳細な認証制御

import com.okta.authn.sdk.client.AuthenticationClient;
import com.okta.authn.sdk.client.AuthenticationClients;
import com.okta.authn.sdk.resource.AuthenticationRequest;
import com.okta.authn.sdk.resource.AuthenticationResponse;

public class OktaAuthenticationExample {
    
    private AuthenticationClient authClient;
    
    public OktaAuthenticationExample() {
        this.authClient = AuthenticationClients.builder()
            .setOrgUrl("https://your-org.okta.com")
            .build();
    }
    
    public void authenticateUser(String username, String password) {
        try {
            AuthenticationRequest request = authClient.instantiate(AuthenticationRequest.class)
                .setUsername(username)
                .setPassword(password.toCharArray());
            
            AuthenticationResponse response = authClient.authenticate(request, null);
            
            switch (response.getStatus()) {
                case SUCCESS:
                    System.out.println("認証成功");
                    String sessionToken = response.getSessionToken();
                    // sessionTokenを使ってOktaセッションを作成
                    break;
                    
                case MFA_REQUIRED:
                    System.out.println("MFA認証が必要");
                    handleMfaChallenge(response);
                    break;
                    
                case PASSWORD_EXPIRED:
                    System.out.println("パスワードの更新が必要");
                    handlePasswordReset(response);
                    break;
                    
                default:
                    System.out.println("認証状態: " + response.getStatus());
            }
            
        } catch (Exception e) {
            System.err.println("認証エラー: " + e.getMessage());
        }
    }
    
    private void handleMfaChallenge(AuthenticationResponse response) {
        // MFA要素の選択とチャレンジ処理
        response.getFactors().forEach(factor -> {
            System.out.println("利用可能なMFA: " + factor.getFactorType());
        });
    }
    
    private void handlePasswordReset(AuthenticationResponse response) {
        // パスワードリセット処理
        System.out.println("パスワードリセットが必要です");
    }
}

グループとアプリケーションの管理

import com.okta.sdk.resource.group.Group;
import com.okta.sdk.resource.group.GroupBuilder;
import com.okta.sdk.resource.application.Application;

public class OktaGroupAndAppManagement {
    
    private Client client;
    
    public OktaGroupAndAppManagement(Client client) {
        this.client = client;
    }
    
    public void manageGroups() {
        // グループの作成
        Group group = GroupBuilder.instance()
            .setName("Developers")
            .setDescription("Development team members")
            .buildAndCreate(client);
        
        // ユーザーをグループに追加
        User user = client.getUser("[email protected]");
        user.addToGroup(group.getId());
        
        // グループメンバーの一覧表示
        group.listUsers().forEach(member -> {
            System.out.println("Group member: " + member.getProfile().getLogin());
        });
    }
    
    public void manageApplications() {
        // アプリケーション一覧の取得
        client.listApplications().forEach(app -> {
            System.out.println("Application: " + app.getName());
            System.out.println("Status: " + app.getStatus());
        });
        
        // 特定のアプリケーションにユーザーを割り当て
        Application app = client.getApplication("app-id");
        User user = client.getUser("[email protected]");
        app.assignUserToApplication(user);
    }
}

Identity Engine SDK使用例

import com.okta.idx.sdk.api.client.IDXAuthenticationWrapper;
import com.okta.idx.sdk.api.response.AuthenticationResponse;
import com.okta.idx.sdk.api.model.AuthenticationOptions;

public class IdentityEngineExample {
    
    private IDXAuthenticationWrapper authWrapper;
    
    public IdentityEngineExample() {
        this.authWrapper = new IDXAuthenticationWrapper(
            "your-client-id",
            "your-client-secret",
            "https://your-org.okta.com",
            "http://localhost:8080/callback"
        );
    }
    
    public void performAuthentication(String username, String password) {
        try {
            AuthenticationOptions authOptions = new AuthenticationOptions(username, password);
            AuthenticationResponse response = authWrapper.authenticate(authOptions, null);
            
            if (response.isLoginSuccessful()) {
                System.out.println("認証成功");
                String accessToken = response.getTokenResponse().getAccessToken();
                System.out.println("Access Token: " + accessToken);
            } else {
                System.out.println("追加の認証ステップが必要: " + response.getAuthenticationStatus());
            }
            
        } catch (Exception e) {
            System.err.println("認証エラー: " + e.getMessage());
        }
    }
}

エラーハンドリングとログ設定

import com.okta.sdk.error.ResourceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class OktaErrorHandling {
    
    private static final Logger logger = LoggerFactory.getLogger(OktaErrorHandling.class);
    private Client client;
    
    public void handleOktaErrors() {
        try {
            User user = client.getUser("[email protected]");
        } catch (ResourceException e) {
            switch (e.getStatus()) {
                case 404:
                    logger.warn("ユーザーが見つかりません: {}", e.getMessage());
                    break;
                case 429:
                    logger.warn("レート制限に達しました: {}", e.getMessage());
                    // リトライロジックの実装
                    break;
                case 401:
                    logger.error("認証エラー: {}", e.getMessage());
                    break;
                default:
                    logger.error("Oktaエラー ({}): {}", e.getStatus(), e.getMessage());
            }
        } catch (Exception e) {
            logger.error("予期しないエラー: {}", e.getMessage(), e);
        }
    }
}

設定ファイル管理

// ~/.okta/okta.yaml
/*
okta:
  client:
    orgUrl: "https://your-org.okta.com"
    token: "your-api-token"
    
# または環境変数
# OKTA_CLIENT_ORGURL=https://your-org.okta.com
# OKTA_CLIENT_TOKEN=your-api-token
*/

@Configuration
public class OktaConfig {
    
    @Bean
    public Client oktaClient() {
        return Clients.builder()
            .setOrgUrl(System.getenv("OKTA_CLIENT_ORGURL"))
            .setClientId(System.getenv("OKTA_CLIENT_CLIENTID"))
            .setPrivateKey(System.getenv("OKTA_CLIENT_PRIVATE_KEY"))
            .build();
    }
}