Skip to content

Commit

Permalink
Client credentials flow
Browse files Browse the repository at this point in the history
  • Loading branch information
m-mohr committed Feb 13, 2024
1 parent c8d8505 commit 04886ce
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 10 deletions.
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Added
* internal functions to serialize and load the session information like connection, process collection, etc.
* Support for the OpenID Connect Client Credentials flow

## Changed
* argument bounding box tries to extract the EPSG code it EPSGCode was provided as WKT2
Expand Down
40 changes: 40 additions & 0 deletions R/authentication.R
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ BasicAuth <- R6Class(
#' \itemize{
#' \item{authorization_code}
#' \item{authorization_code+pkce}
#' \item{urn:ietf:params:oauth:grant-type:device_code}
#' \item{urn:ietf:params:oauth:grant-type:device_code+pkce}
#' }
#'
Expand Down Expand Up @@ -616,6 +617,45 @@ OIDCAuthCodeFlow <- R6Class(
)
)

# [OIDCClientCredentialsFlow] ----
OIDCClientCredentialsFlow <- R6Class(
"OIDCClientCredentialsFlow",
inherit = AbstractOIDCAuthentication,
# public ====
public = list(
# functions ####
login = function() {

client <- oauth_client(
id = private$client_id,
secret = private$secret,
token_url = private$endpoints$token_endpoint,
name = "openeo-r-oidc-auth"
)

private$auth = oauth_flow_client_credentials(
client = client,
scope = paste0(private$scopes, collapse = " ")
)

invisible(self)
}
),
# private ====
private = list(
# attributes ####
grant_type = "client_credentials", # not used internally by httr2, but maybe useful in openeo

# functions ####
isGrantTypeSupported = function(grant_types) {
if (!"client_credentials" %in% grant_types) {
stop("Client Credentials flow is not supported by the authentication provider")
}
invisible(TRUE)
}
)
)

# utility functions ----
.get_oidc_provider = function(provider, oidc_providers=NULL) {
if (is.null(oidc_providers)) {
Expand Down
27 changes: 18 additions & 9 deletions R/client.R
Original file line number Diff line number Diff line change
Expand Up @@ -375,9 +375,11 @@ OpenEOClient <- R6Class(
# probably fetch resolve the potential string into a provider here
provider = .get_oidc_provider(provider)

auth_code = "authorization_code"
auth_pkce = "authorization_code+pkce"
device_pkce = "urn:ietf:params:oauth:grant-type:device_code+pkce"
device_code = "urn:ietf:params:oauth:grant-type:device_code"
client_credentials = "client_credentials"

has_default_clients = "default_clients" %in% names(provider) && length(provider[["default_clients"]]) > 0
client_id_given = "client_id" %in% names(config)
Expand All @@ -388,14 +390,18 @@ OpenEOClient <- R6Class(
}

full_credentials = all(c("client_id","secret") %in% names(config))
is_auth_code = length(config$grant_type) > 0 && config$grant_type == 'authorization_code'
is_auth_code = length(config$grant_type) > 0 && config$grant_type == auth_code
is_client_credentials = length(config$grant_type) > 0 && config$grant_type == client_credentials

# either credentials are set and / or authorization_code as grant_type
if (full_credentials && (is_auth_code || is.null(config$grant_type))) {
# either credentials are set and / or authorization_code or client_credentials as grant_type
if (full_credentials && is_client_credentials) {
private$auth_client = OIDCClientCredentialsFlow$new(provider = provider, config = config, force=TRUE)
}
else if (full_credentials && (is_auth_code || is.null(config$grant_type))) {
private$auth_client = OIDCAuthCodeFlow$new(provider = provider, config = config, force=TRUE)
}
else if (is_auth_code) {
stop("For grant type 'authorization_code' a client_id and secret must be provided")
else if (is_auth_code || is_client_credentials) {
stop("For grant type 'authorization_code' and 'client_credentials' a client_id and secret must be provided")
}
else if (client_id_given && has_default_clients) {
default_clients = provider[["default_clients"]]
Expand Down Expand Up @@ -439,12 +445,15 @@ OpenEOClient <- R6Class(
stop("Please provide a client id or a valid combination of client_id and grant_type.")
}
}

if (device_pkce == config$grant_type) {

has_grant = "grant_type" %in% names(config)
if (has_grant && device_pkce == config$grant_type) {
private$auth_client = OIDCDeviceCodeFlowPkce$new(provider=provider, config = config)
} else if (device_code == config$grant_type) {
} else if (has_grant && device_code == config$grant_type) {
private$auth_client = OIDCDeviceCodeFlow$new(provider=provider, config = config)
} else if (is.null(config$grant_type) || auth_pkce == config$grant_type) {
} else if (has_grant && client_credentials == config$grant_type) {
private$auth_client = OIDCClientCredentialsFlow$new(provider=provider, config = config)
} else if (is.null(config$grant_type) || (has_grant && auth_pkce == config$grant_type)) {
private$auth_client = OIDCAuthCodeFlowPKCE$new(provider=provider, config = config)
}

Expand Down
6 changes: 6 additions & 0 deletions man/OIDCAuth.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions man/login.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion vignettes/openeo-03-package-software-architecture.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ R6 is an object oriented programming style like S3 or S4 in R that is based on [

# Authentication

To get access to the computation capabilities and user stored data openEO you need to be a registered user at the openEO back-end, where you want to carry out your analysis. In order to proof that to the system you need to be authenticated. The openEO API offers Open ID Connect (OIDC) and Basic Authentication as authentication methods. In this package the we use primarily OIDC, but also offer Basic Authentication for legacy support. As OIDC is based on OAuth2 there are several different sub mechanisms, e.g. Authcode Flow or Device Code Flow with or without PKCE. The mechanisms are covered by the httr2 package `httr2::oauth_flow_device()`, which is used to negotiate the authentication and to obtain the required access token. In the code the different authentication methods are realized by the different R6 classes that share a common interface `IAuth`. Inheriting classes implement and overload those functions so that by using the function `..$login()` or the active field access_token all objects behave in the same manor and ultimately provide the access token.
To get access to the computation capabilities and user stored data openEO you need to be a registered user at the openEO back-end, where you want to carry out your analysis. In order to proof that to the system you need to be authenticated. The openEO API offers Open ID Connect (OIDC) and Basic Authentication as authentication methods. In this package the we use primarily OIDC, but also offer Basic Authentication for legacy support. As OIDC is based on OAuth2 there are several different sub mechanisms, e.g. Auth Code Flow or Device Code Flow with or without PKCE. The mechanisms are covered by the httr2 package `httr2::oauth_flow_device()`, which is used to negotiate the authentication and to obtain the required access token. In the code the different authentication methods are realized by the different R6 classes that share a common interface `IAuth`. Inheriting classes implement and overload those functions so that by using the function `..$login()` or the active field access_token all objects behave in the same manor and ultimately provide the access token.

# Visual Components

Expand Down

0 comments on commit 04886ce

Please sign in to comment.