Search code examples
javagoogle-apigoogle-oauthgoogle-admin-sdkgoogle-classroom

Exception in thread "main" com.google.api.client.auth.oauth2.TokenResponseException: 401 Unauthorized when trying to access Google classroom API


I have read all the stack overflow questions related to the issue. I got some idea about it but it could not help me to solve this issue.

I have used service account for authorization. Because I don't want to display the authorization screen for my users. So I have created service account and enabled domain-wide delegation authority in GoogleAPI console.

I need to create courses in the user's google classroom in a programmatical way. I have done using OAuth client ID and OAuth Client secret by following the java Quickstart. After completing the authorization token has been stored in the project directory. Using this token I can create courses in the authorized user's google classroom. Since I need to create courses in google classroom for many users in my domain. So I used service account. When I tried to implement it, I got the above error.

I have implemented it in the web application. For better understanding and code readability, I created a quickstart maven project and pasted the code. The code is working fine up to the System.out.println("Entering to list course method: "); line.

1) How to solve this issue?

2) Will it ask for authorization of Admin? Because in order to access the users data, we need access token. How can I get access token using service account?

public class ClassroomServiceAccount {

    private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
    private static final String SERVICE_ACCOUNT_EMAIL = "xxxxxxx@projectName.iam.gserviceaccount.com";
    private static final String SERVICE_ACCOUNT_PKCS12_FILE_PATH = "/MyProject.p12";
    private static final List<String> SCOPES = Arrays.asList(ClassroomScopes.CLASSROOM_COURSES, ClassroomScopes.CLASSROOM_TOPICS, ClassroomScopes.CLASSROOM_ANNOUNCEMENTS);

    public static void main(String... args) throws IOException, GeneralSecurityException {

        HttpTransport httpTransport = new NetHttpTransport();
        InputStream in = ClassroomServiceAccount.class.getResourceAsStream(SERVICE_ACCOUNT_PKCS12_FILE_PATH);
        KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
        keystore.load(in, "notasecret".toCharArray());

        String alias = "key1";

        Key key = keystore.getKey(alias, "notasecret".toCharArray());

          // Get certificate of public key
          Certificate cert = keystore.getCertificate(alias);

          // Get public key
          PublicKey publicKey = cert.getPublicKey();
          System.out.println("This is public key : "+ publicKey);

          // Return a key pair
          KeyPair k = new KeyPair(publicKey, (PrivateKey) key);
          System.out.println("This is k : "+ k.getPrivate());



    GoogleCredential credential = new GoogleCredential.Builder()
                .setTransport(httpTransport)
                .setJsonFactory(JSON_FACTORY)
                .setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
                .setServiceAccountPrivateKey(k.getPrivate())
                .setServiceAccountScopes(SCOPES)
                .setServiceAccountUser("email@yourdomain.com")
                .build();   


  System.out.println("credentials : " + credential.getServiceAccountPrivateKey());

  Classroom service = new Classroom.Builder(httpTransport, JSON_FACTORY, null)
        .setApplicationName(APPLICATION_NAME)
        .setHttpRequestInitializer(credential)
        .build();

        // Create courses in users google classroom
        Course course = new Course();
        course.setOwnerId("user@yourdomain.com");
        course.setName("Science");
        service.courses().create(course).execute();
        }
    }

Solution

  • I have solved this exception by changing the scopes. i.e., In my code, I have used the three scopes. Those three scopes should be matched with Gsuite's scope. While delegating domain-wide authority to the service account, we added scope. Both these scopes should be matched in order to avoid 401 unauthorized.