Skip to content

Feature: Authorization support #297

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions mcp/src/main/java/io/modelcontextprotocol/auth/AccessToken.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package io.modelcontextprotocol.auth;

import java.util.List;

/**
* Represents an OAuth access token.
*/
public class AccessToken {

private String token;

private String clientId;

private List<String> scopes;

private Integer expiresAt;

public AccessToken() {
}

public AccessToken(String token, String clientId, List<String> scopes, Integer expiresAt) {
this.token = token;
this.clientId = clientId;
this.scopes = scopes;
this.expiresAt = expiresAt;
}

public String getToken() {
return token;
}

public void setToken(String token) {
this.token = token;
}

public String getClientId() {
return clientId;
}

public void setClientId(String clientId) {
this.clientId = clientId;
}

public List<String> getScopes() {
return scopes;
}

public void setScopes(List<String> scopes) {
this.scopes = scopes;
}

public Integer getExpiresAt() {
return expiresAt;
}

public void setExpiresAt(Integer expiresAt) {
this.expiresAt = expiresAt;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package io.modelcontextprotocol.auth;

import java.net.URI;
import java.util.List;

/**
* Represents an OAuth authorization code.
*/
public class AuthorizationCode {

private String code;

private List<String> scopes;

private double expiresAt;

private String clientId;

private String codeChallenge;

private URI redirectUri;

private boolean redirectUriProvidedExplicitly;

public AuthorizationCode() {
}

public AuthorizationCode(String code, List<String> scopes, double expiresAt, String clientId, String codeChallenge,
URI redirectUri, boolean redirectUriProvidedExplicitly) {
this.code = code;
this.scopes = scopes;
this.expiresAt = expiresAt;
this.clientId = clientId;
this.codeChallenge = codeChallenge;
this.redirectUri = redirectUri;
this.redirectUriProvidedExplicitly = redirectUriProvidedExplicitly;
}

public String getCode() {
return code;
}

public void setCode(String code) {
this.code = code;
}

public List<String> getScopes() {
return scopes;
}

public void setScopes(List<String> scopes) {
this.scopes = scopes;
}

public double getExpiresAt() {
return expiresAt;
}

public void setExpiresAt(double expiresAt) {
this.expiresAt = expiresAt;
}

public String getClientId() {
return clientId;
}

public void setClientId(String clientId) {
this.clientId = clientId;
}

public String getCodeChallenge() {
return codeChallenge;
}

public void setCodeChallenge(String codeChallenge) {
this.codeChallenge = codeChallenge;
}

public URI getRedirectUri() {
return redirectUri;
}

public void setRedirectUri(URI redirectUri) {
this.redirectUri = redirectUri;
}

public boolean isRedirectUriProvidedExplicitly() {
return redirectUriProvidedExplicitly;
}

public void setRedirectUriProvidedExplicitly(boolean redirectUriProvidedExplicitly) {
this.redirectUriProvidedExplicitly = redirectUriProvidedExplicitly;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package io.modelcontextprotocol.auth;

import java.net.URI;
import java.util.List;

/**
* Parameters for an authorization request.
*/
public class AuthorizationParams {

private String state;

private List<String> scopes;

private String codeChallenge;

private URI redirectUri;

private boolean redirectUriProvidedExplicitly;

public AuthorizationParams() {
}

public AuthorizationParams(String state, List<String> scopes, String codeChallenge, URI redirectUri,
boolean redirectUriProvidedExplicitly) {
this.state = state;
this.scopes = scopes;
this.codeChallenge = codeChallenge;
this.redirectUri = redirectUri;
this.redirectUriProvidedExplicitly = redirectUriProvidedExplicitly;
}

public String getState() {
return state;
}

public void setState(String state) {
this.state = state;
}

public List<String> getScopes() {
return scopes;
}

public void setScopes(List<String> scopes) {
this.scopes = scopes;
}

public String getCodeChallenge() {
return codeChallenge;
}

public void setCodeChallenge(String codeChallenge) {
this.codeChallenge = codeChallenge;
}

public URI getRedirectUri() {
return redirectUri;
}

public void setRedirectUri(URI redirectUri) {
this.redirectUri = redirectUri;
}

public boolean isRedirectUriProvidedExplicitly() {
return redirectUriProvidedExplicitly;
}

public void setRedirectUriProvidedExplicitly(boolean redirectUriProvidedExplicitly) {
this.redirectUriProvidedExplicitly = redirectUriProvidedExplicitly;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package io.modelcontextprotocol.auth;

/**
* Exception thrown when a redirect URI is invalid.
*/
public class InvalidRedirectUriException extends Exception {

public InvalidRedirectUriException(String message) {
super(message);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package io.modelcontextprotocol.auth;

/**
* Exception thrown when a requested scope is invalid.
*/
public class InvalidScopeException extends Exception {

public InvalidScopeException(String message) {
super(message);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package io.modelcontextprotocol.auth;

import io.modelcontextprotocol.auth.exception.AuthorizeException;
import io.modelcontextprotocol.auth.exception.RegistrationException;
import io.modelcontextprotocol.auth.exception.TokenException;

import java.util.List;
import java.util.concurrent.CompletableFuture;

/**
* Interface for OAuth authorization server providers.
*/
public interface OAuthAuthorizationServerProvider {

/**
* Retrieves client information by client ID.
* @param clientId The ID of the client to retrieve.
* @return A CompletableFuture that resolves to the client information, or null if the
* client does not exist.
*/
CompletableFuture<OAuthClientInformation> getClient(String clientId);

/**
* Saves client information as part of registering it.
* @param clientInfo The client metadata to register.
* @return A CompletableFuture that completes when the registration is done.
* @throws RegistrationException If the client metadata is invalid.
*/
CompletableFuture<Void> registerClient(OAuthClientInformation clientInfo) throws RegistrationException;

/**
* Called as part of the /authorize endpoint, and returns a URL that the client will
* be redirected to.
* @param client The client requesting authorization.
* @param params The parameters of the authorization request.
* @return A CompletableFuture that resolves to a URL to redirect the client to for
* authorization.
* @throws AuthorizeException If the authorization request is invalid.
*/
CompletableFuture<String> authorize(OAuthClientInformation client, AuthorizationParams params)
throws AuthorizeException;

/**
* Loads an AuthorizationCode by its code.
* @param client The client that requested the authorization code.
* @param authorizationCode The authorization code to get the challenge for.
* @return A CompletableFuture that resolves to the AuthorizationCode, or null if not
* found.
*/
CompletableFuture<AuthorizationCode> loadAuthorizationCode(OAuthClientInformation client, String authorizationCode);

/**
* Exchanges an authorization code for an access token and refresh token.
* @param client The client exchanging the authorization code.
* @param authorizationCode The authorization code to exchange.
* @return A CompletableFuture that resolves to the OAuth token, containing access and
* refresh tokens.
* @throws TokenException If the request is invalid.
*/
CompletableFuture<OAuthToken> exchangeAuthorizationCode(OAuthClientInformation client,
AuthorizationCode authorizationCode) throws TokenException;

/**
* Loads a RefreshToken by its token string.
* @param client The client that is requesting to load the refresh token.
* @param refreshToken The refresh token string to load.
* @return A CompletableFuture that resolves to the RefreshToken object if found, or
* null if not found.
*/
CompletableFuture<RefreshToken> loadRefreshToken(OAuthClientInformation client, String refreshToken);

/**
* Exchanges a refresh token for an access token and refresh token.
* @param client The client exchanging the refresh token.
* @param refreshToken The refresh token to exchange.
* @param scopes Optional scopes to request with the new access token.
* @return A CompletableFuture that resolves to the OAuth token, containing access and
* refresh tokens.
* @throws TokenException If the request is invalid.
*/
CompletableFuture<OAuthToken> exchangeRefreshToken(OAuthClientInformation client, RefreshToken refreshToken,
List<String> scopes) throws TokenException;

/**
* Loads an access token by its token.
* @param token The access token to verify.
* @return A CompletableFuture that resolves to the AccessToken, or null if the token
* is invalid.
*/
CompletableFuture<AccessToken> loadAccessToken(String token);

/**
* Revokes an access or refresh token.
* @param token The token to revoke.
* @return A CompletableFuture that completes when the token is revoked.
*/
CompletableFuture<Void> revokeToken(Object token);

}
Loading