Skip to content

Commit

Permalink
Documents functions and updates readme
Browse files Browse the repository at this point in the history
  • Loading branch information
andyquinterom committed Dec 15, 2023
1 parent 804f3dd commit 29e56a3
Show file tree
Hide file tree
Showing 33 changed files with 646 additions and 84 deletions.
3 changes: 3 additions & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
^LICENSE\.md$
^_pkgdown\.yml$
^docs$
^pkgdown$
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,4 @@ po/*~
rsconnect/

example/.Renviron
docs
24 changes: 0 additions & 24 deletions NAMESPACE
Original file line number Diff line number Diff line change
@@ -1,35 +1,11 @@
# Generated by roxygen2: do not edit by hand

S3method(access_token,entra_id_config)
S3method(access_token,google_config)
S3method(decode_token,entra_id_config)
S3method(decode_token,google_config)
S3method(get_client_id,entra_id_config)
S3method(get_client_id,google_config)
S3method(get_login_url,entra_id_config)
S3method(get_login_url,google_config)
S3method(get_logout_url,entra_id_config)
S3method(get_logout_url,google_config)
S3method(print,access_token)
S3method(request_token,entra_id_config)
S3method(request_token,google_config)
S3method(shiny_app,entra_id_config)
S3method(shiny_app,google_config)
export(access_token)
export(decode_token)
export(expires_at)
export(expires_in)
export(get_access_token)
export(get_bearer)
export(get_client_id)
export(get_login_url)
export(get_logout_url)
export(get_token_field)
export(is_expired)
export(is_valid)
export(new_openid_config)
export(refresh_jwks)
export(request_token)
export(shiny_app)
export(sso_shiny_app)
export(token)
80 changes: 68 additions & 12 deletions R/auth.R
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
#' @export
access_token <- function(config, token) {
#' @title Create a new Access Token
#' @description Creates a new access token from a config and a token string
#'
#' @param config An openid_config object
#' @param token_str A string containing the access token to decode
#'
#' @return An access_token object
#' @keywords internal
access_token <- function(config, token_str) {
UseMethod("access_token")
}

#' @export
access_token.google_config <- function(config, token) {
token_data <- decode_token(config, token)
#' @keywords internal
access_token.google_config <- function(config, token_str) {
token_data <- decode_token(config, token_str)
structure(
list(
access_token = token,
access_token = token_str,
exp = lubridate::as_datetime(token_data$exp),
iat = lubridate::as_datetime(token_data$iat),
token_data = token_data
Expand All @@ -17,12 +24,12 @@ access_token.google_config <- function(config, token) {
)
}

#' @export
access_token.entra_id_config <- function(config, token) {
token_data <- decode_token(config, token)
#' @keywords internal
access_token.entra_id_config <- function(config, token_str) {
token_data <- decode_token(config, token_str)
structure(
list(
access_token = token,
access_token = token_str,
exp = lubridate::as_datetime(token_data$exp),
iat = lubridate::as_datetime(token_data$iat),
token_data = token_data
Expand All @@ -31,6 +38,11 @@ access_token.entra_id_config <- function(config, token) {
)
}

#' @title Print an access token
#' @description Prints an access token's expiration date
#'
#' @param x An access_token object
#' @param ... Ignored
#' @export
print.access_token <- function(x, ...) {
expiration_date <- expires_at(x)
Expand All @@ -47,38 +59,82 @@ print.access_token <- function(x, ...) {
"\n",
sep = " "
)
return()
}

#' @title Check if an access token is valid
#' @description Checks if an access token is valid
#' by checking if it is expired
#'
#' @param token An access_token object
#'
#' @return A boolean indicating if the token is valid
#' @export
is_valid <- function(token) {
!is_expired(token)
}

#' @title Check if an access token is expired
#' @description Checks if an access token is expired
#'
#' @param token An access_token object
#'
#' @return A boolean indicating if the token is expired
#' @export
is_expired <- function(token) {
Sys.time() > token$exp
}

#' @export
#' @title Get the Authorization header for an access token
#' @description Gets the Authorization header for an access token
#'
#' @param token An access_token object
#'
#' @return A string containing the Authorization header
#' @keywords internal
get_bearer <- function(token) {
paste0("Bearer ", token$access_token)
}

#' @export
#' @title Get the access token string
#' @description Gets the access token string
#'
#' @param token An access_token object
#'
#' @return A string containing the access token
#' @keywords internal
get_access_token <- function(token) {
token$access_token
}

#' @title Get the expiration time of an access token
#' @description Gets the expiration time of an access token
#'
#' @param token An access_token object
#'
#' @return A duration object containing the time until the token expires
#' @export
expires_in <- function(token) {
token$exp - Sys.time()
}

#' @title Get the expiration date and time of an access token
#' @description Gets the expiration date and time of an access token
#'
#' @param token An access_token object
#'
#' @return A POSIXct object containing the date and time the token expires
#' @export
expires_at <- function(token) {
token$exp
}

#' @title Get the issued at time of an access token
#' @description Gets the issued at time of an access token
#'
#' @param token An access_token object
#'
#' @return A POSIXct object containing the date and time the token was issued
#' @export
get_token_field <- function(token, field) {
token$token_data[[field]]
Expand Down
77 changes: 68 additions & 9 deletions R/config.R
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#' @keywords internal
fetch_jwks <- function(url) {
httr2::request(url) |>
httr2::req_method("GET") |>
Expand All @@ -7,40 +8,98 @@ fetch_jwks <- function(url) {
purrr::map(jose::jwk_read)
}

#' @title New openid configuration
#' @description Creates a new openid configuration object
#' for the given provider
#'
#' @param provider The openid provider to use
#' @param app_url The URL of the application
#' (used to build redirect, login, and logout URLs)
#' @param ... Additional arguments passed to the provider's configuration.
#' This depends on the provider.
#'
#' The `"google"` provider accepts the following arguments:
#' - `client_id`
#' - `client_secret`
#'
#' The `"entra_id"` provider accepts the following arguments:
#' - `client_id`
#' - `client_secret`
#' - `tenant_id`
#'
#' @return An openid_config object
#' @export
new_openid_config <- function(provider, ...) {
new_openid_config <- function(provider, app_url, ...) {
switch(provider,
entra_id = new_entra_id_config(...),
google = new_google_config(...)
entra_id = new_entra_id_config(app_url = app_url, ...),
google = new_google_config(app_url = app_url, ...)
)
}

#' @export
#' @title Get the login URL for the app
#' @description Gets the URL that the provider should redirect to
#' after a login attempt.
#'
#' @param config An openid_config object
#'
#' @return A string containing the login URL
#' @keywords internal
get_login_url <- function(config) {
UseMethod("get_login_url")
}

#' @export

#' @title Get the logout URL for the provider
#' @description Gets the URL for SLO (single logout)
#'
#' @param config An openid_config object
#'
#' @return A string containing the logout URL
#' @keywords internal
get_logout_url <- function(config) {
UseMethod("get_logout_url")
}

#' @export
#' @title Request a token from the provider
#' @description Requests a token from the provider
#'
#' @param config An openid_config object
#' @param authorization_code The authorization code to use
#'
#' @return An access_token object
#' @keywords internal
request_token <- function(config, authorization_code) {
UseMethod("request_token")
}

#' @export
#' @title Decode a token
#' @description Decodes a token
#'
#' @param config An openid_config object
#' @param token The token to decode
#'
#' @return A list containing the decoded token's data
#' @keywords internal
decode_token <- function(config, token) {
UseMethod("decode_token")
}

#' @export
#' @title Get the client ID
#' @description Gets the client ID for the provider
#'
#' @param config An openid_config object
#'
#' @return A string containing the client ID
#' @keywords internal
get_client_id <- function(config) {
UseMethod("get_client_id")
}

#' @export
#' @title Refresh the JWKS
#' @description Refreshes the JWKS
#'
#' @param config An openid_config object
#' @keywords internal
refresh_jwks <- function(config) {
UseMethod("refresh_jwks")
}
14 changes: 8 additions & 6 deletions R/entra_id.R
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
ENTRA_ID_BASE_URL <- "https://login.microsoftonline.com"

#' @keywords internal
build_entra_id_login_url <- function(auth_url, client_id, redirect_uri) {
url <- httr2::url_parse(auth_url)
url$query <- list(
Expand All @@ -13,6 +14,7 @@ build_entra_id_login_url <- function(auth_url, client_id, redirect_uri) {
httr2::url_build(url)
}

#' @keywords internal
new_entra_id_config <- function(tenant_id, client_id, client_secret, app_url) {
auth_url <- glue::glue("{ENTRA_ID_BASE_URL}/{tenant_id}/oauth2/v2.0/authorize")
token_url <- glue::glue("{ENTRA_ID_BASE_URL}/{tenant_id}/oauth2/v2.0/token")
Expand All @@ -35,17 +37,17 @@ new_entra_id_config <- function(tenant_id, client_id, client_secret, app_url) {
)
}

#' @export
#' @keywords internal
get_login_url.entra_id_config <- function(config) {
config$login_url
}

#' @export
#' @keywords internal
get_logout_url.entra_id_config <- function(config) {
stop("Logout not implemented for Entra ID")
}

#' @export
#' @keywords internal
request_token.entra_id_config <- function(config, authorization_code) {
res <- httr2::request(config$token_url) |>
httr2::req_method("POST") |>
Expand All @@ -65,7 +67,7 @@ request_token.entra_id_config <- function(config, authorization_code) {
access_token(config, resp_body$access_token)
}

#' @export
#' @keywords internal
decode_token.entra_id_config <- function(config, token) {
decoded <- config$jwks |>
purrr::map(function(jwk) {
Expand All @@ -84,12 +86,12 @@ decode_token.entra_id_config <- function(config, token) {
return(decoded)
}

#' @export
#' @keywords internal
get_client_id.entra_id_config <- function(config) {
config$client_id
}

#' @export
#' @keywords internal
shiny_app.entra_id_config <- function(config, app) {
app_handler <- app$httpHandler
login_handler <- function(req) {
Expand Down
Loading

0 comments on commit 29e56a3

Please sign in to comment.