Implemented OAuth2 client info controller, super clients and unlimited
authorization scopes. Enabled using Bearer tokens as user authentication
methods for many controllers including OAuth2 controllers.

Change-Id: I1043164acbe49501210a6fca7f4531d110eb81a5
diff --git a/full/Changes b/full/Changes
index 18e2c34..81f9b88 100644
--- a/full/Changes
+++ b/full/Changes
@@ -7,14 +7,19 @@
     - Implemented OAuth2 revoke token (margaretha)
 	- Updated OAuth2 refresh token implementation (margaretha)
 14/08/2018	
-    - Disallow OAuth2 access token (type Bearer) usage for authentication 
-      in OAuth2 controllers (margaretha)
 	- Implemented revoke all OAuth2 access tokens and authorization codes of client  
 	  users when deregistering/deleting a client (margaretha)
 	- Fixed update OAuth2 access token (margaretha)
 	- Implemented reset client secret (margaretha)
 	- Fixed revoking latest access token when refreshing OAuth2 access token (margaretha)
-	  
+15/08/2018
+    - Implemented OAuth2 client info controller (margaretha)
+    - Implemented update OAuth2 client privilege controller for admins (margaretha)
+    - Implemented unlimited authorization scope for super clients with OAuth2 password grant (margaretha)
+    - Marked native clients implementation to deprecated in favour of super clients (margaretha)
+    - Enabled using Bearer tokens as user authentication tokens (Authorization header value) for many 
+      controllers including OAuth2 controllers (margaretha)
+
 
 # version 0.60.5
 
diff --git a/full/src/main/java/de/ids_mannheim/korap/config/Initializator.java b/full/src/main/java/de/ids_mannheim/korap/config/Initializator.java
index 2d7c285..286195c 100644
--- a/full/src/main/java/de/ids_mannheim/korap/config/Initializator.java
+++ b/full/src/main/java/de/ids_mannheim/korap/config/Initializator.java
@@ -1,8 +1,7 @@
 package de.ids_mannheim.korap.config;
 
 import java.io.IOException;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.EnumSet;
 
 import org.springframework.beans.factory.annotation.Autowired;
 
@@ -35,11 +34,7 @@
     }
 
     private void setInitialAccessScope () {
-        OAuth2Scope[] enums = OAuth2Scope.values();
-        Set<String> scopes = new HashSet<>(enums.length);
-        for (OAuth2Scope s : enums) {
-            scopes.add(s.toString());
-        }
+        EnumSet<OAuth2Scope> scopes = EnumSet.allOf(OAuth2Scope.class);
         accessScopeDao.storeAccessScopes(scopes);
     }
 }
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/constant/OAuth2Scope.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/constant/OAuth2Scope.java
index 7e6c973..c44cc53 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/constant/OAuth2Scope.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/constant/OAuth2Scope.java
@@ -1,8 +1,40 @@
 package de.ids_mannheim.korap.oauth2.constant;
 
 public enum OAuth2Scope {
-
-    OPENID, SEARCH, SERIALIZE_QUERY, MATCH_INFO, CREATE_VC, LIST_VC, EDIT_VC, VC_INFO, CLIENT_INFO;
+    
+    ALL, 
+    ADMIN,
+    
+    OPENID, 
+    AUTHORIZE,
+    
+    CLIENT_INFO,
+    REGISTER_CLIENT,
+    DEREGISTER_CLIENT,
+    RESET_CLIENT_SECRET,
+    
+    SEARCH, 
+    SERIALIZE_QUERY,
+    MATCH_INFO, 
+    
+    USER_GROUP_INFO, 
+    CREATE_USER_GROUP, 
+    DELETE_USER_GROUP, 
+    
+    DELETE_USER_GROUP_MEMBER, 
+    ADD_USER_GROUP_MEMBER, 
+    
+    ADD_USER_GROUP_MEMBER_ROLE, 
+    DELETE_USER_GROUP_MEMBER_ROLE, 
+           
+    CREATE_VC, 
+    VC_INFO,
+    EDIT_VC, 
+    DELETE_VC, 
+    
+    SHARE_VC, 
+    DELETE_VC_ACCESS, 
+    VC_ACCESS_INFO;
 
     @Override
     public String toString () {
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/AccessScopeDao.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/AccessScopeDao.java
index a94b506..aa158a3 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/AccessScopeDao.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/AccessScopeDao.java
@@ -13,6 +13,7 @@
 import org.springframework.stereotype.Repository;
 import org.springframework.transaction.annotation.Transactional;
 
+import de.ids_mannheim.korap.oauth2.constant.OAuth2Scope;
 import de.ids_mannheim.korap.oauth2.entity.AccessScope;
 
 @Repository
@@ -33,10 +34,10 @@
         return q.getResultList();
     }
 
-    public void storeAccessScopes (Set<String> scopes) {
+    public void storeAccessScopes (Set<OAuth2Scope> scopes) {
         List<AccessScope> existingScopes = retrieveAccessScopes();
         AccessScope newScope;
-        for (String scope : scopes) {
+        for (OAuth2Scope scope : scopes) {
             newScope = new AccessScope(scope);
             if (!existingScopes.contains(newScope)) {
                 entityManager.persist(newScope);
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/OAuth2ClientDao.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/OAuth2ClientDao.java
index 2b8a2eb..31fa2b3 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/OAuth2ClientDao.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/OAuth2ClientDao.java
@@ -27,9 +27,9 @@
     private EntityManager entityManager;
 
     public void registerClient (String id, String secretHashcode, String name,
-            OAuth2ClientType type, boolean isNative, String url,
-            int urlHashCode, String redirectURI, String registeredBy,
-            String description) throws KustvaktException {
+            OAuth2ClientType type, String url, int urlHashCode,
+            String redirectURI, String registeredBy, String description)
+            throws KustvaktException {
         ParameterChecker.checkStringValue(id, "client id");
         ParameterChecker.checkStringValue(name, "client name");
         ParameterChecker.checkObjectValue(type, "client type");
@@ -44,7 +44,6 @@
         client.setName(name);
         client.setSecret(secretHashcode);
         client.setType(type);
-        client.setNative(isNative);
         if (urlHashCode != 0) {
             OAuth2ClientUrl clientUrl = new OAuth2ClientUrl();
             clientUrl.setUrl(url);
@@ -84,7 +83,8 @@
         }
     }
 
-    public void deregisterClient (OAuth2Client client) throws KustvaktException {
+    public void deregisterClient (OAuth2Client client)
+            throws KustvaktException {
         ParameterChecker.checkObjectValue(client, "client");
         if (!entityManager.contains(client)) {
             client = entityManager.merge(client);
diff --git a/full/src/main/java/de/ids_mannheim/korap/dto/OAuth2ClientDto.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/dto/OAuth2ClientDto.java
similarity index 94%
rename from full/src/main/java/de/ids_mannheim/korap/dto/OAuth2ClientDto.java
rename to full/src/main/java/de/ids_mannheim/korap/oauth2/dto/OAuth2ClientDto.java
index b714404..3f6199b 100644
--- a/full/src/main/java/de/ids_mannheim/korap/dto/OAuth2ClientDto.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/dto/OAuth2ClientDto.java
@@ -1,4 +1,4 @@
-package de.ids_mannheim.korap.dto;
+package de.ids_mannheim.korap.oauth2.dto;
 
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonInclude.Include;
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/dto/OAuth2ClientInfoDto.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/dto/OAuth2ClientInfoDto.java
new file mode 100644
index 0000000..85003ae
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/dto/OAuth2ClientInfoDto.java
@@ -0,0 +1,89 @@
+package de.ids_mannheim.korap.oauth2.dto;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+
+import de.ids_mannheim.korap.oauth2.constant.OAuth2ClientType;
+import de.ids_mannheim.korap.oauth2.entity.OAuth2Client;
+
+@JsonInclude(Include.NON_EMPTY)
+public class OAuth2ClientInfoDto {
+
+    private String id;
+    private String name;
+    private String description;
+    private String isSuper;
+    private String redirectURI;
+    private String registeredBy;
+    private OAuth2ClientType type;
+
+    public OAuth2ClientInfoDto (OAuth2Client client) {
+        this.id = client.getId();
+        this.name = client.getName();
+        this.description = client.getDescription();
+        this.setType(client.getType());
+        this.redirectURI = client.getRedirectURI();
+        this.registeredBy = client.getRegisteredBy();
+
+        if (client.isSuper()) {
+            this.isSuper = "true";
+        }
+    }
+
+    public String getId () {
+        return id;
+    }
+
+    public void setId (String id) {
+        this.id = id;
+    }
+
+    public String getName () {
+        return name;
+    }
+
+    public void setName (String name) {
+        this.name = name;
+    }
+
+    public String getDescription () {
+        return description;
+    }
+
+    public void setDescription (String description) {
+        this.description = description;
+    }
+
+    public String getIsSuper () {
+        return isSuper;
+    }
+
+    public void setIsSuper (String isSuper) {
+        this.isSuper = isSuper;
+    }
+
+    public String getRedirectURI () {
+        return redirectURI;
+    }
+
+    public void setRedirectURI (String redirectURI) {
+        this.redirectURI = redirectURI;
+    }
+
+    public String getRegisteredBy () {
+        return registeredBy;
+    }
+
+    public void setRegisteredBy (String registeredBy) {
+        this.registeredBy = registeredBy;
+    }
+
+    public OAuth2ClientType getType () {
+        return type;
+    }
+
+    public void setType (OAuth2ClientType type) {
+        this.type = type;
+    }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/AccessScope.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/AccessScope.java
index f7951d9..b26a90a 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/AccessScope.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/AccessScope.java
@@ -4,11 +4,14 @@
 import java.util.List;
 
 import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
 import javax.persistence.FetchType;
 import javax.persistence.Id;
 import javax.persistence.ManyToMany;
 import javax.persistence.Table;
 
+import de.ids_mannheim.korap.oauth2.constant.OAuth2Scope;
 import lombok.Getter;
 import lombok.Setter;
 
@@ -21,11 +24,12 @@
     private static final long serialVersionUID = -7356877266702636705L;
 
     @Id
-    private String id;
+    @Enumerated(EnumType.STRING)
+    private OAuth2Scope id;
 
     public AccessScope () {}
 
-    public AccessScope (String scope) {
+    public AccessScope (OAuth2Scope scope) {
         this.id = scope;
     }
 
@@ -37,7 +41,7 @@
 
     @Override
     public String toString () {
-        return id;
+        return id.toString();
     }
 
     @Override
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/OAuth2Client.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/OAuth2Client.java
index 0fb0688..09c6e35 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/OAuth2Client.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/OAuth2Client.java
@@ -12,15 +12,11 @@
 import javax.persistence.Table;
 
 import de.ids_mannheim.korap.oauth2.constant.OAuth2ClientType;
-import lombok.Getter;
-import lombok.Setter;
 
 /**
  * @author margaretha
  *
  */
-@Getter
-@Setter
 @Entity
 @Table(name = "oauth2_client")
 public class OAuth2Client {
@@ -32,23 +28,95 @@
     private String secret;
     @Enumerated(EnumType.STRING)
     private OAuth2ClientType type;
-    @Column(name = "native")
-    private boolean isNative;
+    @Column(name = "super")
+    private boolean isSuper;
     @Column(name = "redirect_uri")
     private String redirectURI;
     @Column(name = "registered_by")
     private String registeredBy;
     private String description;
 
-    @OneToOne(fetch = FetchType.LAZY, cascade=CascadeType.ALL)
+    @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
     @JoinColumn(name = "url_id")
     private OAuth2ClientUrl clientUrl;
 
     @Override
     public String toString () {
         return "id=" + id + ", name=" + name + ", secret=" + secret + ", type="
-                + type + ", isNative=" + isNative + ", redirectURI="
+                + type + ", isSuper=" + isSuper() + ", redirectURI="
                 + redirectURI + ", registeredBy=" + registeredBy
                 + ", description=" + description;
     }
+
+    public boolean isSuper () {
+        return isSuper;
+    }
+
+    public void setSuper (boolean isSuper) {
+        this.isSuper = isSuper;
+    }
+
+    public String getId () {
+        return id;
+    }
+
+    public void setId (String id) {
+        this.id = id;
+    }
+
+    public String getName () {
+        return name;
+    }
+
+    public void setName (String name) {
+        this.name = name;
+    }
+
+    public String getSecret () {
+        return secret;
+    }
+
+    public void setSecret (String secret) {
+        this.secret = secret;
+    }
+
+    public OAuth2ClientType getType () {
+        return type;
+    }
+
+    public void setType (OAuth2ClientType type) {
+        this.type = type;
+    }
+
+    public String getRedirectURI () {
+        return redirectURI;
+    }
+
+    public void setRedirectURI (String redirectURI) {
+        this.redirectURI = redirectURI;
+    }
+
+    public String getRegisteredBy () {
+        return registeredBy;
+    }
+
+    public void setRegisteredBy (String registeredBy) {
+        this.registeredBy = registeredBy;
+    }
+
+    public String getDescription () {
+        return description;
+    }
+
+    public void setDescription (String description) {
+        this.description = description;
+    }
+
+    public OAuth2ClientUrl getClientUrl () {
+        return clientUrl;
+    }
+
+    public void setClientUrl (OAuth2ClientUrl clientUrl) {
+        this.clientUrl = clientUrl;
+    }
 }
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuAuthorizationService.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuAuthorizationService.java
index 7ce105f..cbd638d 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuAuthorizationService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuAuthorizationService.java
@@ -1,9 +1,7 @@
 package de.ids_mannheim.korap.oauth2.oltu.service;
 
-import java.io.UnsupportedEncodingException;
 import java.net.URI;
 import java.net.URISyntaxException;
-import java.net.URLEncoder;
 import java.time.ZonedDateTime;
 
 import javax.servlet.http.HttpServletRequest;
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuTokenService.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuTokenService.java
index bb9baa5..16ff581 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuTokenService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuTokenService.java
@@ -2,6 +2,7 @@
 
 import java.time.ZoneId;
 import java.time.ZonedDateTime;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
@@ -211,14 +212,16 @@
 
         OAuth2Client client =
                 clientService.authenticateClient(clientId, clientSecret);
-        if (!client.isNative()) {
+        if (!client.isSuper()) {
             throw new KustvaktException(StatusCodes.CLIENT_AUTHORIZATION_FAILED,
                     "Password grant is not allowed for third party clients",
                     OAuth2Error.UNAUTHORIZED_CLIENT);
         }
 
         if (scopes == null || scopes.isEmpty()) {
-            scopes = config.getDefaultAccessScopes();
+            scopes = new HashSet<String>(1);
+            scopes.add("all");
+//            scopes = config.getDefaultAccessScopes();
         }
 
         ZonedDateTime authenticationTime =
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/openid/service/OpenIdAuthorizationService.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/openid/service/OpenIdAuthorizationService.java
index 341ff97..5997ca0 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/openid/service/OpenIdAuthorizationService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/openid/service/OpenIdAuthorizationService.java
@@ -111,9 +111,6 @@
             if (scope != null) {
                 scopeSet = new HashSet<>(scope.toStringList());
             }
-            else {
-                scopeSet = config.getDefaultAccessScopes();
-            }
             createAuthorization(username, clientId, redirectUriStr, scopeSet,
                     code.getValue(), authenticationTime, nonce);
         }
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/openid/service/OpenIdTokenService.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/openid/service/OpenIdTokenService.java
index 91e724a..a17b4c8 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/openid/service/OpenIdTokenService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/openid/service/OpenIdTokenService.java
@@ -165,7 +165,7 @@
                     clientCredentials[1]);
         }
 
-        if (!client.isNative()) {
+        if (!client.isSuper()) {
             throw new KustvaktException(StatusCodes.CLIENT_AUTHORIZATION_FAILED,
                     "Password grant is not allowed for third party clients",
                     OAuth2Error.UNAUTHORIZED_CLIENT);
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ClientService.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ClientService.java
index 53027f4..cdd6a66 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ClientService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ClientService.java
@@ -13,7 +13,6 @@
 
 import de.ids_mannheim.korap.config.FullConfiguration;
 import de.ids_mannheim.korap.dao.AdminDao;
-import de.ids_mannheim.korap.dto.OAuth2ClientDto;
 import de.ids_mannheim.korap.encryption.RandomCodeGenerator;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
 import de.ids_mannheim.korap.exceptions.StatusCodes;
@@ -22,6 +21,8 @@
 import de.ids_mannheim.korap.oauth2.constant.OAuth2Error;
 import de.ids_mannheim.korap.oauth2.dao.AccessTokenDao;
 import de.ids_mannheim.korap.oauth2.dao.OAuth2ClientDao;
+import de.ids_mannheim.korap.oauth2.dto.OAuth2ClientDto;
+import de.ids_mannheim.korap.oauth2.dto.OAuth2ClientInfoDto;
 import de.ids_mannheim.korap.oauth2.entity.AccessToken;
 import de.ids_mannheim.korap.oauth2.entity.Authorization;
 import de.ids_mannheim.korap.oauth2.entity.OAuth2Client;
@@ -86,7 +87,7 @@
                     redirectURI + " is invalid.", OAuth2Error.INVALID_REQUEST);
         }
 
-        boolean isNative = isNativeClient(url, redirectURI);
+        // boolean isNative = isNativeClient(url, redirectURI);
 
         String secret = null;
         String secretHashcode = null;
@@ -111,8 +112,8 @@
         String id = codeGenerator.createRandomCode();
         try {
             clientDao.registerClient(id, secretHashcode, clientJson.getName(),
-                    clientJson.getType(), isNative, url, urlHashCode,
-                    redirectURI, registeredBy, clientJson.getDescription());
+                    clientJson.getType(), url, urlHashCode, redirectURI,
+                    registeredBy, clientJson.getDescription());
         }
         catch (Exception e) {
             Throwable cause = e;
@@ -131,7 +132,7 @@
         return new OAuth2ClientDto(id, secret);
     }
 
-
+    @Deprecated
     private boolean isNativeClient (String url, String redirectURI)
             throws KustvaktException {
         if (url == null || url.isEmpty() || redirectURI == null
@@ -170,7 +171,6 @@
         return true;
     }
 
-
     public void deregisterClient (String clientId, String clientSecret,
             String username) throws KustvaktException {
 
@@ -185,13 +185,13 @@
             clientDao.deregisterClient(client);
 
             // revoke all related authorization tokens
-            List<Authorization> authList = authorizationDao
-                    .retrieveAuthorizationsByClientId(clientId);
-            for (Authorization authorization : authList){
+            List<Authorization> authList =
+                    authorizationDao.retrieveAuthorizationsByClientId(clientId);
+            for (Authorization authorization : authList) {
                 authorization.setRevoked(true);
                 authorizationDao.updateAuthorization(authorization);
             }
-            
+
             // revoke all related access tokens
             List<AccessToken> tokens =
                     tokenDao.retrieveAccessTokenByClientId(clientId);
@@ -212,8 +212,7 @@
 
         OAuth2Client client = authenticateClient(clientId, clientSecret);
         if (!client.getType().equals(OAuth2ClientType.CONFIDENTIAL)) {
-            throw new KustvaktException(
-                    StatusCodes.NOT_ALLOWED,
+            throw new KustvaktException(StatusCodes.NOT_ALLOWED,
                     "Operation is not allowed for public clients",
                     OAuth2Error.INVALID_REQUEST);
         }
@@ -259,6 +258,14 @@
                         OAuth2Error.INVALID_REQUEST);
             }
         }
+        else if (client.getSecret() == null || client.getSecret().isEmpty()){
+            if (client.getType().equals(OAuth2ClientType.CONFIDENTIAL)){
+                throw new KustvaktException(
+                        StatusCodes.CLIENT_AUTHENTICATION_FAILED,
+                        "Client secret was not registered",
+                        OAuth2Error.INVALID_CLIENT);
+            }
+        }
         else if (!encryption.checkHash(clientSecret, client.getSecret(),
                 config.getPasscodeSaltField())) {
             throw new KustvaktException(
@@ -267,7 +274,6 @@
         }
     }
 
-
     public OAuth2Client authenticateClientId (String clientId)
             throws KustvaktException {
         if (clientId == null || clientId.isEmpty()) {
@@ -279,4 +285,36 @@
 
         return clientDao.retrieveClientById(clientId);
     }
+
+    public void updatePrivilege (String username, String clientId,
+            boolean isSuper) throws KustvaktException {
+
+        if (adminDao.isAdmin(username)) {
+            OAuth2Client client = clientDao.retrieveClientById(clientId);
+            if (isSuper && !client.getType()
+                    .equals(OAuth2ClientType.CONFIDENTIAL)) {
+                throw new KustvaktException(StatusCodes.NOT_ALLOWED,
+                        "Only confidential clients are allowed to be super clients.");
+            }
+            client.setSuper(isSuper);
+            clientDao.updateClient(client);
+        }
+        else {
+            throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
+                    "Unauthorized operation for user: " + username, username);
+        }
+    }
+
+    public OAuth2ClientInfoDto retrieveClientInfo (String username,
+            String clientId) throws KustvaktException {
+        OAuth2Client client = clientDao.retrieveClientById(clientId);
+        if (adminDao.isAdmin(username)
+                || username.equals(client.getRegisteredBy())) {
+            return new OAuth2ClientInfoDto(client);
+        }
+        else {
+            throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
+                    "Unauthorized operation for user: " + username, username);
+        }
+    }
 }
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ScopeService.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ScopeService.java
index c820a30..bfc35c1 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ScopeService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ScopeService.java
@@ -45,8 +45,18 @@
         Set<AccessScope> requestedScopes =
                 new HashSet<AccessScope>(scopes.size());
         int index;
+        OAuth2Scope oauth2Scope = null;
         for (String scope : scopes) {
-            index = definedScopes.indexOf(new AccessScope(scope));
+            try{
+                oauth2Scope = Enum.valueOf(OAuth2Scope.class, scope.toUpperCase());
+            }
+            catch (IllegalArgumentException e) {
+                throw new KustvaktException(StatusCodes.INVALID_SCOPE,
+                        scope + " is an invalid scope",
+                        OAuth2Error.INVALID_SCOPE);
+            }
+            
+            index = definedScopes.indexOf(new AccessScope(oauth2Scope));
             if (index == -1) {
                 throw new KustvaktException(StatusCodes.INVALID_SCOPE,
                         scope + " is an invalid scope",
@@ -88,15 +98,16 @@
         return filteredScopes;
     }
 
-    public void verifyScope (TokenContext context, OAuth2Scope requestScope)
+    public void verifyScope (TokenContext context, OAuth2Scope requiredScope)
             throws KustvaktException {
         if (!adminDao.isAdmin(context.getUsername())
                 && context.getTokenType().equals(TokenType.BEARER)) {
             Map<String, Object> parameters = context.getParameters();
-            String scope = (String) parameters.get(Attributes.SCOPE);
-            if (!scope.contains(requestScope.toString())) {
+            String authorizedScope = (String) parameters.get(Attributes.SCOPE);
+            if (!authorizedScope.contains(OAuth2Scope.ALL.toString())
+                    && !authorizedScope.contains(requiredScope.toString())) {
                 throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
-                        "Scope " + requestScope + " is not authorized");
+                        "Scope " + requiredScope + " is not authorized");
             }
         }
     }
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2Controller.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2Controller.java
index 082f976..ac082a9 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2Controller.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2Controller.java
@@ -27,10 +27,12 @@
 import com.sun.jersey.spi.container.ResourceFilters;
 
 import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.oauth2.constant.OAuth2Scope;
 import de.ids_mannheim.korap.oauth2.oltu.OAuth2AuthorizationRequest;
 import de.ids_mannheim.korap.oauth2.oltu.OAuth2RevokeTokenRequest;
 import de.ids_mannheim.korap.oauth2.oltu.service.OltuAuthorizationService;
 import de.ids_mannheim.korap.oauth2.oltu.service.OltuTokenService;
+import de.ids_mannheim.korap.oauth2.service.OAuth2ScopeService;
 import de.ids_mannheim.korap.security.context.TokenContext;
 import de.ids_mannheim.korap.web.OAuth2ResponseHandler;
 import de.ids_mannheim.korap.web.filter.AuthenticationFilter;
@@ -47,6 +49,8 @@
     private OltuTokenService tokenService;
     @Autowired
     private OltuAuthorizationService authorizationService;
+    @Autowired
+    private OAuth2ScopeService scopeService;
 
     /**
      * Requests an authorization code.
@@ -73,7 +77,6 @@
     @Path("authorize")
     @ResourceFilters({ AuthenticationFilter.class, BlockingFilter.class })
     @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
-    @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
     public Response requestAuthorizationCode (
             @Context HttpServletRequest request,
             @Context SecurityContext context, @FormParam("state") String state,
@@ -82,10 +85,12 @@
         TokenContext tokenContext = (TokenContext) context.getUserPrincipal();
         String username = tokenContext.getUsername();
         ZonedDateTime authTime = tokenContext.getAuthenticationTime();
-
-        HttpServletRequest requestWithForm =
-                new FormRequestWrapper(request, form);
+        
         try {
+            scopeService.verifyScope(tokenContext, OAuth2Scope.AUTHORIZE);
+        
+            HttpServletRequest requestWithForm =
+                    new FormRequestWrapper(request, form);
             OAuth2AuthorizationRequest authzRequest =
                     new OAuth2AuthorizationRequest(requestWithForm);
             String uri = authorizationService.requestAuthorizationCode(
@@ -218,7 +223,6 @@
     @POST
     @Path("revoke")
     @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
-    @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
     public Response revokeAccessToken (@Context HttpServletRequest request,
             MultivaluedMap<String, String> form) {
 
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2WithOpenIdController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2WithOpenIdController.java
index 43e940b..e9aa803 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2WithOpenIdController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2WithOpenIdController.java
@@ -32,12 +32,14 @@
 import com.sun.jersey.spi.container.ResourceFilters;
 
 import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.oauth2.constant.OAuth2Scope;
 import de.ids_mannheim.korap.oauth2.openid.OpenIdConfiguration;
 import de.ids_mannheim.korap.oauth2.openid.OpenIdHttpRequestWrapper;
 import de.ids_mannheim.korap.oauth2.openid.service.JWKService;
 import de.ids_mannheim.korap.oauth2.openid.service.OpenIdAuthorizationService;
 import de.ids_mannheim.korap.oauth2.openid.service.OpenIdConfigService;
 import de.ids_mannheim.korap.oauth2.openid.service.OpenIdTokenService;
+import de.ids_mannheim.korap.oauth2.service.OAuth2ScopeService;
 import de.ids_mannheim.korap.security.context.TokenContext;
 import de.ids_mannheim.korap.web.OpenIdResponseHandler;
 import de.ids_mannheim.korap.web.filter.AuthenticationFilter;
@@ -56,7 +58,9 @@
     private JWKService jwkService;
     @Autowired
     private OpenIdConfigService configService;
-
+    @Autowired
+    private OAuth2ScopeService scopeService;
+    
     @Autowired
     private OpenIdResponseHandler openIdResponseHandler;
 
@@ -135,6 +139,8 @@
 
         URI uri = null;
         try {
+            scopeService.verifyScope(tokenContext, OAuth2Scope.AUTHORIZE);
+            
             if (isAuthentication) {
                 authzService.checkRedirectUriParam(map);
             }
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuthClientController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuthClientController.java
index 293685e..387194c 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuthClientController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuthClientController.java
@@ -3,6 +3,7 @@
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
 import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
@@ -17,31 +18,50 @@
 
 import com.sun.jersey.spi.container.ResourceFilters;
 
-import de.ids_mannheim.korap.dto.OAuth2ClientDto;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.oauth2.constant.OAuth2Scope;
+import de.ids_mannheim.korap.oauth2.dto.OAuth2ClientDto;
+import de.ids_mannheim.korap.oauth2.dto.OAuth2ClientInfoDto;
 import de.ids_mannheim.korap.oauth2.service.OAuth2ClientService;
+import de.ids_mannheim.korap.oauth2.service.OAuth2ScopeService;
 import de.ids_mannheim.korap.security.context.TokenContext;
 import de.ids_mannheim.korap.web.OAuth2ResponseHandler;
 import de.ids_mannheim.korap.web.filter.AuthenticationFilter;
 import de.ids_mannheim.korap.web.filter.BlockingFilter;
 import de.ids_mannheim.korap.web.input.OAuth2ClientJson;
 
-
 /**
  * Defines controllers for OAuth2 clients, namely applications
  * performing actions such as searching and retrieving match
  * information on behalf of users.
  * 
+ * <br /><br />
+ * According to its privileges, clients are categorized into super and
+ * normal clients. Super clients are intended only for clients that
+ * are part of KorAP. They has special privileges to use controllers
+ * that usually are not allowed for normal clients, for instance using
+ * OAuth2 password grant to obtain access tokens.
+ * 
+ * <br /><br />
+ * By default, clients are set as normal clients. Super clients has to
+ * be set manually by an admin, e.g by using
+ * {@link #updateClientPrivilege(SecurityContext, String, boolean)}
+ * controller. Only confidential clients are allowed to be super
+ * clients.
+ * 
  * @author margaretha
  *
  */
 @Controller
 @Path("oauth2/client")
+@ResourceFilters({ AuthenticationFilter.class, BlockingFilter.class })
 public class OAuthClientController {
 
     @Autowired
     private OAuth2ClientService clientService;
     @Autowired
+    private OAuth2ScopeService scopeService;
+    @Autowired
     private OAuth2ResponseHandler responseHandler;
 
     /**
@@ -70,13 +90,13 @@
     @Path("register")
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
-    @ResourceFilters({ AuthenticationFilter.class, BlockingFilter.class })
     public OAuth2ClientDto registerClient (
             @Context SecurityContext securityContext,
             OAuth2ClientJson clientJson) {
         TokenContext context =
                 (TokenContext) securityContext.getUserPrincipal();
         try {
+            scopeService.verifyScope(context, OAuth2Scope.REGISTER_CLIENT);
             return clientService.registerClient(clientJson,
                     context.getUsername());
         }
@@ -85,7 +105,6 @@
         }
     }
 
-
     /**
      * Deregisters a client requires client owner authentication. For
      * confidential clients, client authentication is also required.
@@ -101,7 +120,6 @@
     @DELETE
     @Path("deregister/{client_id}")
     @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
-    @ResourceFilters({ AuthenticationFilter.class, BlockingFilter.class })
     public Response deregisterPublicClient (
             @Context SecurityContext securityContext,
             @PathParam("client_id") String clientId,
@@ -109,6 +127,7 @@
         TokenContext context =
                 (TokenContext) securityContext.getUserPrincipal();
         try {
+            scopeService.verifyScope(context, OAuth2Scope.DEREGISTER_CLIENT);
             clientService.deregisterClient(clientId, clientSecret,
                     context.getUsername());
             return Response.ok().build();
@@ -132,7 +151,6 @@
     @Path("reset")
     @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
     @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
-    @ResourceFilters({ AuthenticationFilter.class, BlockingFilter.class })
     public OAuth2ClientDto resetClientSecret (
             @Context SecurityContext securityContext,
             @FormParam("client_id") String clientId,
@@ -140,6 +158,7 @@
         TokenContext context =
                 (TokenContext) securityContext.getUserPrincipal();
         try {
+            scopeService.verifyScope(context, OAuth2Scope.RESET_CLIENT_SECRET);
             return clientService.resetSecret(clientId, clientSecret,
                     context.getUsername());
         }
@@ -148,4 +167,51 @@
         }
     }
 
+    /**
+     * Facilitates editing client privileges for admin purposes, e.g.
+     * setting a specific client to be a super client, and vice versa.
+     * Only confidential clients are allowed to be super clients.
+     * 
+     * @param securityContext
+     * @param clientId
+     * @param super true indicating super client, false otherwise
+     * @return Response status OK, if successful 
+     */
+    @POST
+    @Path("privilege")
+    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+    public Response updateClientPrivilege (
+            @Context SecurityContext securityContext,
+            @FormParam("client_id") String clientId,
+            @FormParam("super") String isSuper) {
+        TokenContext context =
+                (TokenContext) securityContext.getUserPrincipal();
+        try {
+            scopeService.verifyScope(context, OAuth2Scope.ADMIN);
+            clientService.updatePrivilege(context.getUsername(), clientId,
+                    Boolean.valueOf(isSuper));
+            return Response.ok().build();
+        }
+        catch (KustvaktException e) {
+            throw responseHandler.throwit(e);
+        }
+    }
+
+    @GET
+    @Path("info/{client_id}")
+    @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
+    public OAuth2ClientInfoDto retrieveClientInfo (
+            @Context SecurityContext securityContext,
+            @PathParam("client_id") String clientId) {
+        TokenContext context =
+                (TokenContext) securityContext.getUserPrincipal();
+        try {
+            scopeService.verifyScope(context, OAuth2Scope.CLIENT_INFO);
+            return clientService.retrieveClientInfo(context.getUsername(),
+                    clientId);
+        }
+        catch (KustvaktException e) {
+            throw responseHandler.throwit(e);
+        }
+    }
 }
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupController.java
index a0850ce..8e82de0 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupController.java
@@ -24,6 +24,8 @@
 import de.ids_mannheim.korap.constant.UserGroupStatus;
 import de.ids_mannheim.korap.dto.UserGroupDto;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.oauth2.constant.OAuth2Scope;
+import de.ids_mannheim.korap.oauth2.service.OAuth2ScopeService;
 import de.ids_mannheim.korap.security.context.TokenContext;
 import de.ids_mannheim.korap.service.UserGroupService;
 import de.ids_mannheim.korap.web.KustvaktResponseHandler;
@@ -54,6 +56,8 @@
     private KustvaktResponseHandler kustvaktResponseHandler;
     @Autowired
     private UserGroupService service;
+    @Autowired
+    private OAuth2ScopeService scopeService;
 
     /**
      * Returns all user-groups in which a user is an active or a
@@ -74,6 +78,7 @@
         TokenContext context =
                 (TokenContext) securityContext.getUserPrincipal();
         try {
+            scopeService.verifyScope(context, OAuth2Scope.USER_GROUP_INFO);
             return service.retrieveUserGroup(context.getUsername());
         }
         catch (KustvaktException e) {
@@ -106,6 +111,7 @@
         TokenContext context =
                 (TokenContext) securityContext.getUserPrincipal();
         try {
+            scopeService.verifyScope(context, OAuth2Scope.ADMIN);
             return service.retrieveUserGroupByStatus(username,
                     context.getUsername(), status);
         }
@@ -131,6 +137,7 @@
         TokenContext context =
                 (TokenContext) securityContext.getUserPrincipal();
         try {
+            scopeService.verifyScope(context, OAuth2Scope.ADMIN);
             return service.searchById(context.getUsername(), groupId);
         }
         catch (KustvaktException e) {
@@ -168,6 +175,7 @@
         TokenContext context =
                 (TokenContext) securityContext.getUserPrincipal();
         try {
+            scopeService.verifyScope(context, OAuth2Scope.CREATE_USER_GROUP);
             service.createUserGroup(group, context.getUsername());
             return Response.ok().build();
         }
@@ -178,8 +186,7 @@
 
     /**
      * Deletes a user-group specified by the group id. Only group
-     * owner
-     * and system admins can delete groups.
+     * owner and system admins can delete groups.
      * 
      * @param securityContext
      * @param groupId
@@ -192,6 +199,7 @@
         TokenContext context =
                 (TokenContext) securityContext.getUserPrincipal();
         try {
+            scopeService.verifyScope(context, OAuth2Scope.DELETE_USER_GROUP);
             service.deleteGroup(groupId, context.getUsername());
             return Response.ok().build();
         }
@@ -219,6 +227,7 @@
         TokenContext context =
                 (TokenContext) securityContext.getUserPrincipal();
         try {
+            scopeService.verifyScope(context, OAuth2Scope.DELETE_USER_GROUP_MEMBER);
             service.deleteGroupMember(memberId, groupId, context.getUsername());
             return Response.ok().build();
         }
@@ -247,6 +256,7 @@
         TokenContext context =
                 (TokenContext) securityContext.getUserPrincipal();
         try {
+            scopeService.verifyScope(context, OAuth2Scope.ADD_USER_GROUP_MEMBER);
             service.inviteGroupMembers(group, context.getUsername());
             return Response.ok().build();
         }
@@ -279,6 +289,7 @@
         TokenContext context =
                 (TokenContext) securityContext.getUserPrincipal();
         try {
+            scopeService.verifyScope(context, OAuth2Scope.ADD_USER_GROUP_MEMBER_ROLE);
             service.addMemberRoles(context.getUsername(), groupId,
                     memberUsername, roleIds);
             return Response.ok().build();
@@ -290,8 +301,7 @@
 
     /**
      * Deletes roles of a member of a user-group. Only user-group
-     * admins
-     * and system admins are allowed.
+     * admins and system admins are allowed.
      * 
      * @param securityContext
      * @param groupId
@@ -312,6 +322,7 @@
         TokenContext context =
                 (TokenContext) securityContext.getUserPrincipal();
         try {
+            scopeService.verifyScope(context, OAuth2Scope.DELETE_USER_GROUP_MEMBER_ROLE);
             service.deleteMemberRoles(context.getUsername(), groupId,
                     memberUsername, roleIds);
             return Response.ok().build();
@@ -339,6 +350,7 @@
         TokenContext context =
                 (TokenContext) securityContext.getUserPrincipal();
         try {
+            scopeService.verifyScope(context, OAuth2Scope.ADD_USER_GROUP_MEMBER);
             service.acceptInvitation(groupId, context.getUsername());
             return Response.ok().build();
         }
@@ -367,6 +379,7 @@
         TokenContext context =
                 (TokenContext) securityContext.getUserPrincipal();
         try {
+            scopeService.verifyScope(context, OAuth2Scope.DELETE_USER_GROUP_MEMBER);
             service.deleteGroupMember(context.getUsername(), groupId,
                     context.getUsername());
             return Response.ok().build();
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusController.java
index 24d6e52..2627bdc 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusController.java
@@ -21,13 +21,11 @@
 
 import com.sun.jersey.spi.container.ResourceFilters;
 
-import de.ids_mannheim.korap.constant.TokenType;
 import de.ids_mannheim.korap.constant.VirtualCorpusAccessStatus;
 import de.ids_mannheim.korap.constant.VirtualCorpusType;
 import de.ids_mannheim.korap.dto.VirtualCorpusAccessDto;
 import de.ids_mannheim.korap.dto.VirtualCorpusDto;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
-import de.ids_mannheim.korap.exceptions.StatusCodes;
 import de.ids_mannheim.korap.oauth2.constant.OAuth2Scope;
 import de.ids_mannheim.korap.oauth2.service.OAuth2ScopeService;
 import de.ids_mannheim.korap.security.context.TokenContext;
@@ -229,11 +227,7 @@
         TokenContext context =
                 (TokenContext) securityContext.getUserPrincipal();
         try {
-            if (context.getTokenType().equals(TokenType.BEARER)){
-                throw new KustvaktException(
-                        StatusCodes.AUTHENTICATION_FAILED,
-                        "Token type Bearer is not allowed");   
-            }
+            scopeService.verifyScope(context, OAuth2Scope.ADMIN);
             return service.listVCByType(context.getUsername(), createdBy, type);
         }
         catch (KustvaktException e) {
@@ -258,6 +252,7 @@
         TokenContext context =
                 (TokenContext) securityContext.getUserPrincipal();
         try {
+            scopeService.verifyScope(context, OAuth2Scope.DELETE_VC);
             service.deleteVC(context.getUsername(), vcId);
         }
         catch (KustvaktException e) {
@@ -289,6 +284,7 @@
         TokenContext context =
                 (TokenContext) securityContext.getUserPrincipal();
         try {
+            scopeService.verifyScope(context, OAuth2Scope.SHARE_VC);
             service.shareVC(context.getUsername(), vcId, groupId);
         }
         catch (KustvaktException e) {
@@ -315,6 +311,7 @@
         TokenContext context =
                 (TokenContext) securityContext.getUserPrincipal();
         try {
+            scopeService.verifyScope(context, OAuth2Scope.DELETE_VC_ACCESS);
             service.deleteVCAccess(accessId, context.getUsername());
         }
         catch (KustvaktException e) {
@@ -348,6 +345,7 @@
         TokenContext context =
                 (TokenContext) securityContext.getUserPrincipal();
         try {
+            scopeService.verifyScope(context, OAuth2Scope.VC_ACCESS_INFO);
             return service.listVCAccessByVC(context.getUsername(), vcId);
         }
         catch (KustvaktException e) {
@@ -377,6 +375,7 @@
         TokenContext context =
                 (TokenContext) securityContext.getUserPrincipal();
         try {
+            scopeService.verifyScope(context, OAuth2Scope.VC_ACCESS_INFO);
             return service.listVCAccessByGroup(context.getUsername(), groupId);
         }
         catch (KustvaktException e) {
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/filter/AuthenticationFilter.java b/full/src/main/java/de/ids_mannheim/korap/web/filter/AuthenticationFilter.java
index d0a6cf6..57d232d 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/filter/AuthenticationFilter.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/filter/AuthenticationFilter.java
@@ -75,15 +75,6 @@
                         context = authenticationManager.getTokenContext(
                                 TokenType.BEARER, authData.getToken(), host,
                                 ua);
-                        if (request.getPath().startsWith("oauth2")
-                                || request.getPath().startsWith("vc/access")
-                                || request.getPath().startsWith("vc/delete")
-                                || request.getPath().startsWith("group")
-                                || request.getPath().startsWith("user")) {
-                            throw new KustvaktException(
-                                    StatusCodes.AUTHORIZATION_FAILED,
-                                    "Token type Bearer is not allowed");
-                        }
                         break;
                     // EM: JWT token-based authentication scheme
                     case API:
diff --git a/full/src/main/resources/db/insert/V3.5__insert_oauth2_clients.sql b/full/src/main/resources/db/insert/V3.5__insert_oauth2_clients.sql
index dd55405..68bbfa7 100644
--- a/full/src/main/resources/db/insert/V3.5__insert_oauth2_clients.sql
+++ b/full/src/main/resources/db/insert/V3.5__insert_oauth2_clients.sql
@@ -4,48 +4,48 @@
 VALUES("http://korap.ids-mannheim.de/confidential", 2087150261);
 
 -- plain secret value is "secret"
-INSERT INTO oauth2_client(id,name,secret,type,native,url_id,
+INSERT INTO oauth2_client(id,name,secret,type,super,url_id,
   redirect_uri,registered_by, description) 
 VALUES ("fCBbQkAyYzI4NzUxMg","test confidential client",
   "$2a$08$vi1FbuN3p6GcI1tSxMAoeuIYL8Yw3j6A8wJthaN8ZboVnrQaTwLPq",
   "CONFIDENTIAL", 1, 2087150261,
   "https://korap.ids-mannheim.de/confidential/redirect", "system",
-  "This is a test native confidential client.");
+  "This is a test super confidential client.");
 
   
 INSERT INTO oauth2_client_url(url,url_hashcode)
 VALUES("http://third.party.com/confidential", 1712550103);
 
 -- plain secret value is "secret"
-INSERT INTO oauth2_client(id,name,secret,type,native,url_id,
+INSERT INTO oauth2_client(id,name,secret,type,super,url_id,
   redirect_uri,registered_by, description) 
-VALUES ("9aHsGW6QflV13ixNpez","test non native confidential client",
+VALUES ("9aHsGW6QflV13ixNpez","test non super confidential client",
   "$2a$08$vi1FbuN3p6GcI1tSxMAoeuIYL8Yw3j6A8wJthaN8ZboVnrQaTwLPq",
   "CONFIDENTIAL", 0, 1712550103,
   "https://third.party.com/confidential/redirect", "system",
-  "This is a test nonnative confidential client.");
+  "This is a test nonsuper confidential client.");
 
   
 INSERT INTO oauth2_client_url(url,url_hashcode)
 VALUES("http://third.party.client.com", -2137275617);
 
-INSERT INTO oauth2_client(id,name,secret,type,native,url_id,
+INSERT INTO oauth2_client(id,name,secret,type,super,url_id,
   redirect_uri, registered_by, description) 
 VALUES ("8bIDtZnH6NvRkW2Fq","third party client",null,
   "PUBLIC", 0, -2137275617,
   "https://third.party.client.com/redirect","system",
-  "This is a test nonnative public client.");
+  "This is a test nonsuper public client.");
 
   
 INSERT INTO oauth2_client_url(url,url_hashcode)
 VALUES("http://korap.ids-mannheim.de/public", 1360724310); 
   
-INSERT INTO oauth2_client(id,name,secret,type,native,url_id,
-  redirect_uri, registered_by, description) 
-VALUES ("iBr3LsTCxOj7D2o0A5m","test public client",null,
-  "PUBLIC", 1, 1360724310,
-  "https://korap.ids-mannheim.de/public/redirect","system", 
-  "This is a test native public client."); 
+--INSERT INTO oauth2_client(id,name,secret,type,super,url_id,
+--  redirect_uri, registered_by, description) 
+--VALUES ("iBr3LsTCxOj7D2o0A5m","test public client",null,
+--  "PUBLIC", 1, 1360724310,
+--  "https://korap.ids-mannheim.de/public/redirect","system", 
+--  "This is a test super public client."); 
 
 INSERT INTO oauth2_access_token(token,user_id,created_date, user_auth_time)
 VALUES("fia0123ikBWn931470H8s5gRqx7Moc4p","marlin","2018-05-30 16:25:50",
diff --git a/full/src/main/resources/db/new-sqlite/V1.4__oauth2_tables.sql b/full/src/main/resources/db/new-sqlite/V1.4__oauth2_tables.sql
index dff4df8..47f0a0f 100644
--- a/full/src/main/resources/db/new-sqlite/V1.4__oauth2_tables.sql
+++ b/full/src/main/resources/db/new-sqlite/V1.4__oauth2_tables.sql
@@ -11,7 +11,7 @@
 	name VARCHAR(255) NOT NULL,
 	secret VARCHAR(255) DEFAULT NULL,
 	type VARCHAR(255) NOT NULL,
-	native BOOLEAN DEFAULT FALSE,
+	super BOOLEAN DEFAULT FALSE,
 	redirect_uri TEXT DEFAULT NULL,
 	description VARCHAR(255) NOT NULL,
 	registered_by VARCHAR(100) NOT NULL,
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AccessTokenTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AccessTokenTest.java
index 98f3434..78d91aa 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AccessTokenTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AccessTokenTest.java
@@ -2,22 +2,27 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 
 import java.io.IOException;
+import java.net.URI;
 
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response.Status;
 
 import org.apache.http.entity.ContentType;
 import org.apache.oltu.oauth2.common.message.types.GrantType;
+import org.apache.oltu.oauth2.common.message.types.TokenType;
 import org.junit.Test;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.util.UriComponentsBuilder;
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.google.common.net.HttpHeaders;
 import com.sun.jersey.api.client.ClientResponse;
 import com.sun.jersey.core.util.MultivaluedMapImpl;
 
-import de.ids_mannheim.korap.authentication.http.TransferEncoding;
+import de.ids_mannheim.korap.authentication.http.HttpAuthorizationHandler;
 import de.ids_mannheim.korap.config.Attributes;
 import de.ids_mannheim.korap.config.SpringJerseyTest;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
@@ -27,15 +32,48 @@
 
 public class OAuth2AccessTokenTest extends SpringJerseyTest {
 
-    private String clientId = "fCBbQkAyYzI4NzUxMg";
+    // normal client
+    private String clientId = "9aHsGW6QflV13ixNpez";
+    private String superClientId = "fCBbQkAyYzI4NzUxMg";
+    private String clientSecret = "secret";
 
-    private JsonNode requestToken () throws KustvaktException {
+    private String requestAuthorizationCode (String scope, String authHeader)
+            throws KustvaktException {
         MultivaluedMap<String, String> form = new MultivaluedMapImpl();
-        form.add("grant_type", "password");
+        form.add("response_type", "code");
         form.add("client_id", clientId);
-        form.add("client_secret", "secret");
-        form.add("username", "dory");
-        form.add("password", "password");
+        form.add("client_secret", clientSecret);
+        if (scope != null) {
+            form.add("scope", scope);
+        }
+
+        ClientResponse response = resource().path("oauth2").path("authorize")
+                .header(Attributes.AUTHORIZATION, authHeader)
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .header(HttpHeaders.CONTENT_TYPE,
+                        ContentType.APPLICATION_FORM_URLENCODED)
+                .entity(form).post(ClientResponse.class);
+
+        assertEquals(Status.TEMPORARY_REDIRECT.getStatusCode(),
+                response.getStatus());
+        URI redirectUri = response.getLocation();
+        MultiValueMap<String, String> params = UriComponentsBuilder
+                .fromUri(redirectUri).build().getQueryParams();
+        return params.getFirst("code");
+    }
+
+    // client credentials as form params
+    private JsonNode requestTokenWithAuthorizationCodeGrant ()
+            throws KustvaktException {
+        String authHeader = HttpAuthorizationHandler
+                .createBasicAuthorizationHeaderValue("dory", "password");
+        String code = requestAuthorizationCode(null, authHeader);
+
+        MultivaluedMap<String, String> form = new MultivaluedMapImpl();
+        form.add("grant_type", "authorization_code");
+        form.add("client_id", clientId);
+        form.add("client_secret", clientSecret);
+        form.add("code", code);
 
         ClientResponse response = resource().path("oauth2").path("token")
                 .header(HttpHeaders.CONTENT_TYPE,
@@ -43,45 +81,85 @@
                 .entity(form).post(ClientResponse.class);
 
         String entity = response.getEntity(String.class);
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
         JsonNode node = JsonUtils.readTree(entity);
         return node;
     }
 
-    @Test
-    public void testListVCScope () throws KustvaktException {
+    // client credentials in authorization header
+    private JsonNode requestTokenWithAuthorizationHeader (String code)
+            throws KustvaktException {
         MultivaluedMap<String, String> form = new MultivaluedMapImpl();
-        form.add("grant_type", "password");
-        form.add("client_id", "fCBbQkAyYzI4NzUxMg");
-        form.add("client_secret", "secret");
-        form.add("username", "dory");
-        form.add("password", "password");
-        form.add("scope", OAuth2Scope.VC_INFO.toString());
+        form.add("grant_type", "authorization_code");
+        form.add("client_id", clientId);
+        form.add("code", code);
 
         ClientResponse response = resource().path("oauth2").path("token")
-                .header(HttpHeaders.AUTHORIZATION,
-                        "Bearer" + TransferEncoding
-                                .encodeBase64("fCBbQkAyYzI4NzUxMg", "secret"))
+                .header(Attributes.AUTHORIZATION,
+                        HttpAuthorizationHandler
+                                .createBasicAuthorizationHeaderValue(clientId,
+                                        clientSecret))
                 .header(HttpHeaders.CONTENT_TYPE,
                         ContentType.APPLICATION_FORM_URLENCODED)
                 .entity(form).post(ClientResponse.class);
 
         String entity = response.getEntity(String.class);
-        JsonNode node = JsonUtils.readTree(entity);
-        String token = node.at("/access_token").asText();
+        return JsonUtils.readTree(entity);
+    }
 
-        response = resource().path("vc").path("list")
+    @Test
+    public void testScopeWithSuperClient () throws KustvaktException {
+        MultivaluedMap<String, String> form = new MultivaluedMapImpl();
+        form.add("grant_type", "password");
+        form.add("client_id", superClientId);
+        form.add("client_secret", clientSecret);
+        form.add("username", "dory");
+        form.add("password", "password");
+
+        ClientResponse response = resource().path("oauth2").path("token")
+                .header(HttpHeaders.CONTENT_TYPE,
+                        ContentType.APPLICATION_FORM_URLENCODED)
+                .entity(form).post(ClientResponse.class);
+
+        JsonNode node = JsonUtils.readTree(response.getEntity(String.class));
+        assertEquals("all", node.at("/scope").asText());
+        String accessToken = node.at("/access_token").asText();
+
+        // test list user group
+        response = resource().path("group").path("list")
+                .header(Attributes.AUTHORIZATION, "Bearer " + accessToken)
+                .get(ClientResponse.class);
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        node = JsonUtils.readTree(response.getEntity(String.class));
+        assertEquals(2, node.size());
+    }
+
+    @Test
+    public void testCustomAuthorizationScope () throws KustvaktException {
+        String authHeader = HttpAuthorizationHandler
+                .createBasicAuthorizationHeaderValue("dory", "password");
+        String code = requestAuthorizationCode(OAuth2Scope.VC_INFO.toString(),
+                authHeader);
+        JsonNode node = requestTokenWithAuthorizationHeader(code);
+
+        String token = node.at("/access_token").asText();
+        assertTrue(node.at("/scope").asText()
+                .contains(OAuth2Scope.VC_INFO.toString()));
+
+        ClientResponse response = resource().path("vc").path("list")
                 .header(Attributes.AUTHORIZATION, "Bearer " + token)
                 .get(ClientResponse.class);
 
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
-        entity = response.getEntity(String.class);
-        node = JsonUtils.readTree(entity);
+        node = JsonUtils.readTree(response.getEntity(String.class));
         assertEquals(4, node.size());
     }
 
     @Test
-    public void testTokenAccessScope () throws KustvaktException, IOException {
-        String accessToken = requestToken().at("/access_token").asText();
+    public void testDefaultScope () throws KustvaktException, IOException {
+        String accessToken = requestTokenWithAuthorizationCodeGrant()
+                .at("/access_token").asText();
         testListVCScopeNotAuthorized(accessToken);
         testListVCAccessBearerNotAuthorize(accessToken);
         testSearchWithOAuth2Token(accessToken);
@@ -116,7 +194,7 @@
         JsonNode node = JsonUtils.readTree(entity);
         assertEquals(StatusCodes.AUTHORIZATION_FAILED,
                 node.at("/errors/0/0").asInt());
-        assertEquals("Token type Bearer is not allowed",
+        assertEquals("Scope vc_access_info is not authorized",
                 node.at("/errors/0/1").asText());
     }
 
@@ -161,10 +239,11 @@
     @Test
     public void testRevokeAccessTokenConfidentialClient ()
             throws KustvaktException {
-        String accessToken = requestToken().at("/access_token").asText();
+        String accessToken = requestTokenWithAuthorizationCodeGrant()
+                .at("/access_token").asText();
         MultivaluedMap<String, String> form = new MultivaluedMapImpl();
         form.add("token", accessToken);
-        form.add("client_id", "fCBbQkAyYzI4NzUxMg");
+        form.add("client_id", clientId);
         form.add("client_secret", "secret");
 
         ClientResponse response = resource().path("oauth2").path("revoke")
@@ -200,10 +279,10 @@
     @Test
     public void testRevocationAfterRequestRefreshToken ()
             throws KustvaktException {
-        JsonNode node = requestToken();
+        JsonNode node = requestTokenWithAuthorizationCodeGrant();
         String accessToken = node.at("/access_token").asText();
         String refreshToken = node.at("/refresh_token").asText();
-        
+
         MultivaluedMap<String, String> form = new MultivaluedMapImpl();
         form.add("grant_type", GrantType.REFRESH_TOKEN.toString());
         form.add("client_id", clientId);
@@ -221,8 +300,65 @@
 
         node = JsonUtils.readTree(entity);
         assertNotNull(node.at("/access_token").asText());
-        assertEquals(refreshToken,node.at("/refresh_token").asText());
-        
+        assertEquals(refreshToken, node.at("/refresh_token").asText());
+
         testSearchWithRevokedToken(accessToken);
     }
+
+    @Test
+    public void testRequestAuthorizationWithBearerTokenUnauthorized () throws KustvaktException {
+        String userAuthToken = requestTokenWithAuthorizationCodeGrant()
+                .at("/access_token").asText();
+
+        MultivaluedMap<String, String> form = new MultivaluedMapImpl();
+        form.add("response_type", "code");
+        form.add("client_id", clientId);
+        form.add("client_secret", clientSecret);
+
+        ClientResponse response = resource().path("oauth2").path("authorize")
+                .header(Attributes.AUTHORIZATION, "Bearer " + userAuthToken)
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .header(HttpHeaders.CONTENT_TYPE,
+                        ContentType.APPLICATION_FORM_URLENCODED)
+                .entity(form).post(ClientResponse.class);
+
+        assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
+        
+        JsonNode node = JsonUtils.readTree(response.getEntity(String.class));
+        assertEquals(StatusCodes.AUTHORIZATION_FAILED,
+                node.at("/errors/0/0").asInt());
+        assertEquals("Scope authorize is not authorized",
+                node.at("/errors/0/1").asText());
+    }
+
+    @Test
+    public void testRequestAuthorizationWithBearerToken ()
+            throws KustvaktException {
+        String userAuthToken = requestTokenWithPasswordGrant();
+        String code = requestAuthorizationCode(null, "Bearer " + userAuthToken);
+        assertNotNull(code);
+    }
+
+    private String requestTokenWithPasswordGrant () throws KustvaktException {
+        MultivaluedMap<String, String> form = new MultivaluedMapImpl();
+        form.add("grant_type", "password");
+        form.add("client_id", superClientId);
+        form.add("client_secret", clientSecret);
+        form.add("username", "dory");
+        form.add("password", "password");
+
+        ClientResponse response = resource().path("oauth2").path("token")
+                .header(HttpHeaders.CONTENT_TYPE,
+                        ContentType.APPLICATION_FORM_URLENCODED)
+                .entity(form).post(ClientResponse.class);
+        String entity = response.getEntity(String.class);
+
+        JsonNode node = JsonUtils.readTree(entity);
+        String token = node.at("/access_token").asText();
+        assertNotNull(token);
+        assertEquals(TokenType.BEARER.toString(),
+                node.at("/token_type").asText());
+        assertNotNull(node.at("/expires_in").asText());
+        return token;
+    }
 }
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ClientControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ClientControllerTest.java
index ffecbb3..9c3d536 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ClientControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ClientControllerTest.java
@@ -94,7 +94,6 @@
         testDeregisterConfidentialClient(clientId, newclientSecret);
     }
 
-
     private void testRegisterClientNonUniqueURL () throws KustvaktException {
         ClientResponse response = registerConfidentialClient();
         assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
@@ -127,9 +126,8 @@
         assertNotNull(clientId);
         assertTrue(node.at("/client_secret").isMissingNode());
 
-        testDeregisterPublicClientMissingUserAuthentication(clientId);
-        testDeregisterPublicClientMissingId();
-        testDeregisterPublicClient(clientId);
+        testResetPublicClientSecret(clientId);
+        testAccessTokenAfterDeregistration(clientId);
     }
 
     @Test
@@ -155,35 +153,9 @@
         assertNotNull(clientId);
         assertTrue(node.at("/client_secret").isMissingNode());
 
-        testResetPublicClientSecret(clientId);
-    }
-
-    @Test
-    public void testRegisterNativeClient () throws UniformInterfaceException,
-            ClientHandlerException, KustvaktException {
-        OAuth2ClientJson json = new OAuth2ClientJson();
-        json.setName("NativeClient");
-        json.setType(OAuth2ClientType.PUBLIC);
-        json.setUrl("http://korap.ids-mannheim.de/native");
-        json.setRedirectURI("https://korap.ids-mannheim.de/native/redirect");
-        json.setDescription("This is a native test client.");
-
-        ClientResponse response = resource().path("oauth2").path("client")
-                .path("register")
-                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
-                        .createBasicAuthorizationHeaderValue(username, "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-                .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)
-                .entity(json).post(ClientResponse.class);
-
-        String entity = response.getEntity(String.class);
-        assertEquals(Status.OK.getStatusCode(), response.getStatus());
-        JsonNode node = JsonUtils.readTree(entity);
-        String clientId = node.at("/client_id").asText();
-
-        // EM: need to check native
-
-        testAccessTokenAfterDeregistration(clientId);
+        testDeregisterPublicClientMissingUserAuthentication(clientId);
+        testDeregisterPublicClientMissingId();
+        testDeregisterPublicClient(clientId);
     }
 
     private void testAccessTokenAfterDeregistration (String clientId)
@@ -420,4 +392,55 @@
 
         return newClientSecret;
     }
+
+    @Test
+    public void testUpdateClientPrivilege () throws KustvaktException {
+        ClientResponse response = registerConfidentialClient();
+        JsonNode node = JsonUtils.readTree(response.getEntity(String.class));
+        String clientId = node.at("/client_id").asText();
+
+        MultivaluedMap<String, String> form = new MultivaluedMapImpl();
+        form.add("client_id", clientId);
+        form.add("super", "true");
+
+        updateClientPrivilege(form);
+        node = retrieveClientInfo(clientId, "admin");
+        assertTrue(node.at("/isSuper").asBoolean());
+
+        form.remove("super");
+        form.add("super", "false");
+        updateClientPrivilege(form);
+        node = retrieveClientInfo(clientId, username);
+        assertTrue(node.at("/isSuper").isMissingNode());
+
+    }
+
+    private void updateClientPrivilege (MultivaluedMap<String, String> form)
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
+        ClientResponse response = resource().path("oauth2").path("client")
+                .path("privilege")
+                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+                        .createBasicAuthorizationHeaderValue("admin", "pass"))
+                .header(HttpHeaders.CONTENT_TYPE,
+                        ContentType.APPLICATION_FORM_URLENCODED)
+                .entity(form).post(ClientResponse.class);
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+    }
+
+    private JsonNode retrieveClientInfo (String clientId, String username)
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
+        ClientResponse response = resource().path("oauth2").path("client")
+                .path("info").path(clientId)
+                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+                        .createBasicAuthorizationHeaderValue(username, "pass"))
+                .get(ClientResponse.class);
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+        String entity = response.getEntity(String.class);
+        return JsonUtils.readTree(entity);
+    }
 }
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ControllerTest.java
index 3dabb7b..5d452e8 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ControllerTest.java
@@ -181,6 +181,46 @@
     }
 
     @Test
+    public void testRequestTokenAuthorizationPublic () throws KustvaktException {
+        String clientId = "8bIDtZnH6NvRkW2Fq";
+        MultivaluedMap<String, String> authForm = new MultivaluedMapImpl();
+        authForm.add("response_type", "code");
+        authForm.add("client_id", clientId);
+
+        ClientResponse response = requestAuthorization(authForm);
+        URI redirectUri = response.getLocation();
+        MultiValueMap<String, String> params = UriComponentsBuilder
+                .fromUri(redirectUri).build().getQueryParams();
+        String code = params.get("code").get(0);
+        
+        MultivaluedMap<String, String> tokenForm = new MultivaluedMapImpl();
+        tokenForm.add("grant_type", "authorization_code");
+        tokenForm.add("client_id", clientId);
+        tokenForm.add("code", code);
+
+        response = requestToken(tokenForm);
+        String entity = response.getEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+        
+        String accessToken = node.at("/access_token").asText();
+        String refreshToken = node.at("/refresh_token").asText();
+        
+        assertEquals(TokenType.BEARER.toString(),
+                node.at("/token_type").asText());
+        assertNotNull(node.at("/expires_in").asText());
+        
+        testRevokeTokenPublicClient(accessToken, clientId, "access_token");
+        
+        testRequestRefreshTokenInvalidScope(clientId, refreshToken);
+        testRequestRefreshTokenPublicClient(clientId, refreshToken);
+        testRequestRefreshTokenInvalidClient(refreshToken);
+        testRequestRefreshTokenInvalidRefreshToken(clientId);
+        
+        testRevokeTokenPublicClient(refreshToken, clientId, "refresh_token");
+        testRequestRefreshWithRevokedRefreshToken(clientId, refreshToken);
+    }
+    
+    @Test
     public void testRequestTokenAuthorizationConfidential ()
             throws KustvaktException {
 
@@ -203,7 +243,6 @@
         tokenForm.add("client_id", "fCBbQkAyYzI4NzUxMg");
         tokenForm.add("client_secret", "secret");
         tokenForm.add("code", code);
-        System.out.println(code);
 
         response = requestToken(tokenForm);
         String entity = response.getEntity(String.class);
@@ -437,7 +476,7 @@
     @Test
     public void testRequestTokenPasswordGrantPublic ()
             throws KustvaktException {
-        String clientId = "iBr3LsTCxOj7D2o0A5m";
+        String clientId = "8bIDtZnH6NvRkW2Fq";
         MultivaluedMap<String, String> form = new MultivaluedMapImpl();
         form.add("grant_type", "password");
         form.add("username", "dory");
@@ -447,35 +486,23 @@
         ClientResponse response = requestToken(form);
         String entity = response.getEntity(String.class);
 
-        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
 
         JsonNode node = JsonUtils.readTree(entity);
-        String accessToken = node.at("/access_token").asText();
-        assertEquals(TokenType.BEARER.toString(),
-                node.at("/token_type").asText());
-        assertNotNull(node.at("/expires_in").asText());
-
-
-        testRevokeTokenPublicClient(accessToken, clientId, "access_token");
-
-        String refreshToken = node.at("/refresh_token").asText();
-        testRequestRefreshTokenInvalidScope(clientId, refreshToken);
-        testRequestRefreshTokenPublicClient(clientId, refreshToken);
-        testRequestRefreshTokenInvalidClient(refreshToken);
-        testRequestRefreshTokenInvalidRefreshToken(clientId);
-        
-        testRevokeTokenPublicClient(refreshToken, clientId, "refresh_token");
-        testRequestRefreshWithRevokedRefreshToken(clientId, refreshToken);
+        assertEquals(OAuth2Error.UNAUTHORIZED_CLIENT,
+                node.at("/error").asText());
+        assertEquals("Password grant is not allowed for third party clients",
+                node.at("/error_description").asText());
     }
 
     @Test
-    public void testRequestTokenPasswordGrantNonNative ()
+    public void testRequestTokenPasswordGrantConfidentialNonSuper ()
             throws KustvaktException {
         MultivaluedMap<String, String> form = new MultivaluedMapImpl();
         form.add("grant_type", "password");
         form.add("username", "dory");
         form.add("password", "password");
-        // confidential nonnative
+        // confidential non-super
         form.add("client_id", "9aHsGW6QflV13ixNpez");
         form.add("client_secret", "secret");
 
@@ -639,7 +666,7 @@
             throws KustvaktException {
         MultivaluedMap<String, String> form = new MultivaluedMapImpl();
         form.add("grant_type", GrantType.REFRESH_TOKEN.toString());
-        form.add("client_id", "8bIDtZnH6NvRkW2Fq");
+        form.add("client_id", "iBr3LsTCxOj7D2o0A5m");
         form.add("refresh_token", refreshToken);
 
         ClientResponse response = resource().path("oauth2").path("token")
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2OpenIdControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2OpenIdControllerTest.java
index eb716ab..abb44d7 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2OpenIdControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2OpenIdControllerTest.java
@@ -103,8 +103,6 @@
         assertEquals("thisIsMyState", params.getFirst("state"));
     }
 
-
-
     private void testRequestAuthorizationCodeWithoutOpenID (
             MultivaluedMap<String, String> form, String redirectUri)
             throws KustvaktException {
@@ -350,14 +348,13 @@
         assertEquals(nonce, claimsSet.getClaim("nonce"));
     }
 
-
     // no openid
     @Test
     public void testRequestAccessTokenWithPassword ()
             throws KustvaktException, ParseException, InvalidKeySpecException,
             NoSuchAlgorithmException, JOSEException {
         // public client
-        String client_id = "iBr3LsTCxOj7D2o0A5m";
+        String client_id = "8bIDtZnH6NvRkW2Fq";
         MultivaluedMap<String, String> tokenForm = new MultivaluedMapImpl();
         testRequestAccessTokenMissingGrant(tokenForm);
 
@@ -373,13 +370,12 @@
         ClientResponse tokenResponse = sendTokenRequest(tokenForm);
         String entity = tokenResponse.getEntity(String.class);
         JsonNode node = JsonUtils.readTree(entity);
-        assertNotNull(node.at("/access_token").asText());
-        assertNotNull(node.at("/refresh_token").asText());
-        assertEquals(TokenType.BEARER.toString(),
-                node.at("/token_type").asText());
-        assertNotNull(node.at("/expires_in").asText());
-    }
 
+        assertEquals(OAuth2Error.UNAUTHORIZED_CLIENT,
+                node.at("/error").asText());
+        assertEquals("Password grant is not allowed for third party clients",
+                node.at("/error_description").asText());
+    }
 
     private void testRequestAccessTokenMissingUsername (
             MultivaluedMap<String, String> tokenForm) throws KustvaktException {