-
Notifications
You must be signed in to change notification settings - Fork 2
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
Add OpenIDConnect #5
Comments
An example https://github.com/tanmaykm/OpenIDConnect.jl/blob/master/tools/oidc_standalone.jl Workflow:
|
Sounds reasonable, but only works if the client is run locally. A Julia client that runs on a remote machine for e.g. a notebook will not work this way. Then you need the Device Code Flow (maybe it can be inspired from the openEO Python client implementation). @soxofaan For client ID + secret for openEO Platform, I think you can contact @bschumac . |
Indeed, I would not bother implementing the OIDC authorization code flow, that's a lot of complexity that is not going to work in various use cases anyway. Note: it was the initial OIDC flow implemented in the openeo Python client, but I don't think anybody uses that anymore now that we have better alternatives. We're probably just going to drop the option completely: Open-EO/openeo-python-client#235 instead try to implement these OIDC flows instead:
Both of these are relatively easy in terms of HTTP requests and you don't necessarily need a third party OIDC oriented library for that (e.g. we just use a generic HTTP lib in the openeo python client) |
Thanks for the input! I created a toy example device code flow for a test app of mine. Seems indeed pretty simple: using JSON
using HTTP
function auth_oidc_device_flow(auth_host::String, client_id::String)
url = "$(auth_host)/oauth/device/code"
headers = ["content-type" => "application/x-www-form-urlencoded"]
args = Dict(
"client_id" => client_id,
"scope" => "profile email",
"audience" => "test-api"
)
body = HTTP.URIs.escapeuri(args)
device_code_request = HTTP.post(url, headers, body) |> x -> x.body |> String |> JSON.parse
@info "Please log in using any device at:\n" *
device_code_request["verification_uri_complete"] *
"\nWaiting until log in succeeded..."
while true
url = "$(auth_host)/oauth/token"
headers = ["content-type" => "application/x-www-form-urlencoded"]
args = Dict(
"grant_type" => "urn:ietf:params:oauth:grant-type:device_code",
"device_code" => device_code_request["device_code"],
"client_id" => client_id
)
body = HTTP.URIs.escapeuri(args)
try
access_token = HTTP.post(url, headers, body) |> x -> x.body |> String |> JSON.parse
return access_token
catch e
sleep(device_code_request["interval"])
end
end
end
client_id = "XXXclient_idXXXhuikfdzui"
auth_host = "https://dev-appIDXXXX.us.auth0.com"
access_token = auth_oidc_device_flow(auth_host, client_id) |
Indeed, that looks like the essential part of it. Note that |
EDIT: Works now. Use ' ' instead of ',' to separate scopes in the request After reading RFC7636, I managed to log in into openEO vito cloud using OIDC device flow + PKCE. However, I've got error 403 forbidden after using the access token. Proper enrollment in openEO Platform virtual organization is required. Is this not by default? I'm using a 30 day trail account (yet). # @see https://developers.onelogin.com/openid-connect/guides/auth-flow-pkce
using SHA
using Random
using Base64
using HTTP
using JSON3
function auth_oidc_device_flow_pkce(discovery_url::String, client_id::String, scopes::AbstractVector)
oidc_config = HTTP.get(discovery_url).body |> JSON3.read
code_verifier = randstring(128)
hash = sha256(code_verifier)
# see https://datatracker.ietf.org/doc/html/rfc7636#appendix-B
code_challenge = hash |> base64encode |> x -> strip(x, '=') |> x -> replace(x, "+" => "-", "/" => "_")
headers = ["content-type" => "application/x-www-form-urlencoded"]
args = Dict(
"client_id" => client_id,
"grant_type" => "urn:ietf:params:oauth:grant-type:device_code",
"code_challenge_method" => "S256",
"code_challenge" => code_challenge,
"scope" => join(scopes, " ") # EDIT: Use space instead of ','
)
body = HTTP.URIs.escapeuri(args)
device_code_request = HTTP.post(oidc_config.device_authorization_endpoint, headers, body) |> x -> x.body |> String |> JSON3.read
@info "Please log in using any device at:\n" *
device_code_request.verification_uri_complete *
"\nWaiting until log in succeeded..."
while true
headers = ["content-type" => "application/x-www-form-urlencoded"]
args = Dict(
"grant_type" => "urn:ietf:params:oauth:grant-type:device_code",
"device_code" => device_code_request.device_code,
"client_id" => client_id,
"code_verifier" => code_verifier
)
body = HTTP.URIs.escapeuri(args)
try
access_token = HTTP.post(oidc_config.token_endpoint, headers, body) |>
x -> x.body |> String |> JSON3.read |> x -> x.access_token
return access_token
catch e
sleep(device_code_request.interval)
end
end
end
openeo_oidc_provider =
HTTP.get("https://openeocloud.vito.be/openeo/1.0.0/credentials/oidc").body |>
JSON3.read |>
x -> x.providers[1]
openeo_oidc_discovery_url = "$(openeo_oidc_provider.issuer).well-known/openid-configuration"
# https://aai.egi.eu/auth/realms/egi/.well-known/openid-configuration
openeo_client_id = openeo_oidc_provider.default_clients[1].id
# openeo-platform-default-client
access_token = auth_oidc_device_flow_pkce(openeo_oidc_discovery_url, openeo_client_id, openeo_oidc_provider.scopes)
# string of length 1081
# test
headers = [
"Authorization" => "Bearer oidc/$(openeo_oidc_provider.id)/$access_token"
]
response = HTTP.get("https://openeocloud.vito.be/openeo/1.0.0/jobs", headers)
# HTTP/1.1 403 Forbidden
# {"code":"PermissionsInsufficient","id":"r-88f2795606f84712a71b609aaaaa","message":"Proper enrollment in openEO Platform virtual organization is required."} |
Which scopes did you send to retrieve the token? Does logging in with the Web Editor work? If yes, it's your code, if no, it's your account (and Benjamin needs to help) ;-) |
Try to get it working first with a backend like https://openeo.vito.be/openeo/1.1/ instead of openeocloud. openeocloud has some additional requirements for the EGI account, which are not part of core openEO API |
Huh? What is it? |
These "Proper enrollment in openEO Platform virtual organization is required" checks |
But that should be captured by sending the two additional scopes that are exposed in the endpoint, no? |
Now it seems to work even with https://openeocloud.vito.be. I took all the 4 scopes openid, email, eduperson_entitlement and eduperson_scoped_affiliation. The error was in concatenating the scopes with a , instead of a space. Now, the webpage https://aai.egi.eu/device?user_code=QFNZ-XXXX asks for "Grant Access to openEO Platform" explicitly. So the user needs to click ok, not any admin. |
Many openEO backends, e.g. https://openeocloud.vito.be/openeo/1.0.0/ require OpenIDConnect (OICD) for authentification.
There is OpenIDConnect.jl, but the example only shows a OIDC client in JavaScript. I have not seen a real world implementation of OIDC in julia yet. We also need to register OpenEOClient.jl e.g. at egi.eu to get
client_id
andclient_secret
.The text was updated successfully, but these errors were encountered: