How To Access Federated Api Using Saml2 And Adfs
##########################################################################
# modified from: https://aws.amazon.com/blogs/security/how-to-implement-federated-api-and-cli-access-using-saml-2-0-and-ad-fs/
##########################################################################
##########################################################################
# Packages required
##########################################################################
library(httr)
library(xml2)
# getPass is used within the function when password isn't provided
# base64enc is used to decode samlassertion
##########################################################################
# wrapper to access
adfs_saml2 <- function(adfs_url, region, username = NULL, password = NULL){
# Get the federated credentials from the user
if(is.null(username)) username = readline("Username:")
if(is.null(password)) password = getPass::getPass()
set_config(config(ssl_verifypeer = 0L))
# get first page
formresponse = GET(adfs_url)
on.exit(handle_reset(adfs_url))
idpauthformsubmiturl = formresponse$url
formsoup = content(formresponse)
payload = list()
for (inputtag in xml_find_all(formsoup, ".//INPUT|.//input")){
name = if(is.na(xml_attr(inputtag, "name"))) "" else xml_attr(inputtag, "name")
value = if(is.na(xml_attr(inputtag, "value"))) "" else xml_attr(inputtag, "value")
if(grepl("user", tolower(name)))
payload[[name]] = username
else if(grepl("email", tolower(name)))
payload[[name]] = username
else if(grepl("pass", tolower(name)))
payload[[name]] = password
else
payload[[name]] = value
}
for (inputtag in xml_find_all(formsoup,('.//FORM|.//form'))){
action = xml_attr(inputtag, 'action')
loginid = xml_attr(inputtag, 'id')
if (!is.na(action) && loginid == "loginForm"){
parsedurl = parse_url(adfs_url)
idpauthformsubmiturl = paste0(parsedurl$scheme,"://",parsedurl$hostname,action)
}
}
Cookies = unlist(as.list(formresponse$cookies))
# get samlassertion
response = POST(idpauthformsubmiturl, body = payload, set_cookies(.cookies = Cookies), encode="form")
soup = content(response)
SAMLResponse = NA
for (inputtag in xml_find_all(soup,('.//input'))){
name = if(!is.na(xml_attr(inputtag, 'name'))) xml_attr(inputtag, 'name') else ""
if(name == 'SAMLResponse')
SAMLResponse = xml_attr(inputtag, 'value')
}
if(is.na(SAMLResponse)) stop('Response did not contain a valid SAML assertion')
# decode saml assertion so that can get roles for user to choose from
root = readBin(base64enc::base64decode(SAMLResponse), "character")
saml2attribute = read_xml(root)
# get correct level to iterate over
child3 = xml_children(xml_children(xml_children(saml2attribute)))
# get aws roles
awsroles= list()
for(saml in child3){
if(isTRUE(xml_attr(saml, "Name") == "https://aws.amazon.com/SAML/Attributes/Role"))
awsroles = c(awsroles, xml_text(xml_children(saml)))
}
# safe way to present roles to user (hides saml roles)
aws_saml_role = lapply(seq_along(awsroles), function(i){
aws_role_split = unlist(strsplit(awsroles[[i]], ","))
saml = grepl("saml-provider", aws_role_split)
names(aws_role_split) = c("role_arn", "principal_arn")[as.numeric(saml)+1]
aws_role_split}
)
# asks user which role to choose
writeLines("Please choose the role you would like to assume:")
for(i in seq_along(aws_saml_role)){
writeLines(sprintf("[%i]: %s",i , aws_saml_role[[i]]["role_arn"]))
}
choosen_role = as.numeric(readline("Selection: "))
aws_saml_role=aws_saml_role[[choosen_role]]
list(SamlRole = aws_saml_role, SAMLResponse = SAMLResponse)
}
##########################################################################
# Variables
# region: The default AWS region that this script will connect
# to for all API calls
region = 'us-west-2'
# idpentryurl: The initial URL that starts the authentication process.
idpentryurl = 'https://<fqdn>/adfs/ls/IdpInitiatedSignOn.aspx?loginToRp=urn:amazon:webservices'
##########################################################################
##########################################################################
# connect to adfs and get saml2 assertion
obj = adfs_saml2(idpentryurl, region)
cred= paws::sts()$assume_role_with_saml(RoleArn = obj$SamlRole["role_arn"],
PrincipalArn = obj$SamlRole["principal_arn"],
SAMLAssertion = obj$SAMLResponse)
##########################################################################
# Use the AWS STS token to list all of the S3 buckets
config = list(
credentials = list(
creds = list(
access_key_id = cred$Credentials$AccessKeyId,
secret_access_key = cred$Credentials$SecretAccessKey,
session_token = cred$Credentials$SessionToken
)
)
)
# connect to AWS S3 using temporary SAML credentials
S3= paws::s3(config)
# list all s3 buckets
sapply(S3$list_buckets()$Buckets, function(x) x$Name)