Describe the bug
Given a Portal CR configured with OIDC,
I am expecting a login button in the upper right corner of a Portal page to take me to the login page, where I can click "connect with some OIDC provider"
To Reproduce
export GLOO_PORTAL_VERSION=1.3.0-beta6
#export GLOO_PORTAL_VERSION=1.2.8
export GLOO_VERSION=1.12.13
# DOMAIN NAMES
export PORTAL_FQDN=portal.mydomain.com
export API_FQDN=api.mydomain.com
################
# Gloo EDGE
################
helm repo update
helm upgrade -i gloo glooe/gloo-ee --namespace gloo-system --version ${GLOO_VERSION} \
--create-namespace --set-string license_key="$LICENSE_KEY"
kubectl -n gloo-system wait po --for condition=Ready --timeout -1s --all
##########
# GLOO PORTAL
##########
cat << EOF > portal-values.yaml
glooEdge:
enabled: true
licenseKey:
secretRef:
name: license
namespace: gloo-system
key: license-key
EOF
helm repo update
helm install gloo-portal gloo-portal/gloo-portal -n gloo-portal --values portal-values.yaml --version=${GLOO_PORTAL_VERSION} --create-namespace
kubectl -n gloo-portal wait pod --all --for condition=Ready --timeout -1s
##########
# KEYCLOAK
##########
kubectl create -f https://raw.githubusercontent.com/keycloak/keycloak-quickstarts/12.0.4/kubernetes-examples/keycloak.yaml
kubectl rollout status deploy/keycloak
# Get Keycloak URL and token
KEYCLOAK_URL=http://$(kubectl get service keycloak -o jsonpath='{.status.loadBalancer.ingress[0].ip}'):8080/auth
KEYCLOAK_TOKEN=$(curl -s -d "client_id=admin-cli" -d "username=admin" -d "password=admin" -d "grant_type=password" "$KEYCLOAK_URL/realms/master/protocol/openid-connect/token" | jq -r .access_token)
GLOO_GW_IP=$(glooctl proxy address | cut -d':' -f1)
# Create an initial token to register the client
read -r client token <<<$(curl -s -H "Authorization: Bearer ${KEYCLOAK_TOKEN}" -X POST -H "Content-Type: application/json" -d '{"expiration": 0, "count": 1}' $KEYCLOAK_URL/admin/realms/master/clients-initial-access | jq -r '[.id, .token] | @tsv')
# Register the client
read -r id secret <<<$(curl -X POST -d "{ \"clientId\": \"${client}\" }" -H "Content-Type:application/json" -H "Authorization: bearer ${token}" ${KEYCLOAK_URL}/realms/master/clients-registrations/default| jq -r '[.id, .secret] | @tsv')
# Add allowed redirect URIs
curl -v -H "Authorization: Bearer ${KEYCLOAK_TOKEN}" -X PUT -H "Content-Type: application/json" -d '{"serviceAccountsEnabled": true, "authorizationServicesEnabled": true, "redirectUris": ["https://'${PORTAL_FQDN}'/callback", "http://'${PORTAL_FQDN}'/callback", "http://'${GLOO_GW_IP}'/callback"]}' $KEYCLOAK_URL/admin/realms/master/clients/${id}
# Add the group attribute in the JWT token returned by Keycloak
curl -H "Authorization: Bearer ${KEYCLOAK_TOKEN}" -X POST -H "Content-Type: application/json" -d '{"name": "group", "protocol": "openid-connect", "protocolMapper": "oidc-usermodel-attribute-mapper", "config": {"claim.name": "group", "jsonType.label": "String", "user.attribute": "group", "id.token.claim": "true", "access.token.claim": "true"}}' $KEYCLOAK_URL/admin/realms/master/clients/${id}/protocol-mappers/models
# create groups "users" and "execs"
curl -H "Authorization: Bearer ${KEYCLOAK_TOKEN}" -X POST -H "Content-Type: application/json" -d '{"name": "users"}' $KEYCLOAK_URL/admin/realms/master/groups
curl -H "Authorization: Bearer ${KEYCLOAK_TOKEN}" -X POST -H "Content-Type: application/json" -d '{"name": "execs"}' $KEYCLOAK_URL/admin/realms/master/groups
# Create first user "user1", group: users, mail address: user1@solo.io
curl -H "Authorization: Bearer ${KEYCLOAK_TOKEN}" -X POST -H "Content-Type: application/json" -d '{"username": "user1", "email": "user1@solo.io", "enabled": true, "groups": ["users"], "attributes": {"group": "users"}, "credentials": [{"type": "password", "value": "password", "temporary": false}]}' $KEYCLOAK_URL/admin/realms/master/users
# Create second user "user2", group: users, mail address: user1@example.com
curl -H "Authorization: Bearer ${KEYCLOAK_TOKEN}" -X POST -H "Content-Type: application/json" -d '{"username": "user2", "email": "user2@example.com", "enabled": true, "groups": ["users"], "attributes": {"group": "users"}, "credentials": [{"type": "password", "value": "password", "temporary": false}]}' $KEYCLOAK_URL/admin/realms/master/users
# Create third user "exec1", group: execs, mail address: exec1@solo.io
curl -H "Authorization: Bearer ${KEYCLOAK_TOKEN}" -X POST -H "Content-Type: application/json" -d '{"username": "exec1", "email": "exec1@solo.io", "enabled": true, "groups": ["execs"], "attributes": {"group": "execs"}, "credentials": [{"type": "password", "value": "password", "temporary": false}]}' $KEYCLOAK_URL/admin/realms/master/users
#############
# APIDOCS
############
for i in {1..2}; do
kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: petstore-v$i
spec:
replicas: 1
selector:
matchLabels:
app: petstore
version: v$i
template:
metadata:
labels:
app: petstore
version: v$i
spec:
containers:
- name: petstore
image: swaggerapi/petstore
# env:
# - name: SWAGGER_BASE_PATH
# value: /
imagePullPolicy: Always
ports:
- name: http
containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: petstore-v$i
spec:
ports:
- name: http
port: 8080
targetPort: http
protocol: TCP
selector:
app: petstore
version: v$i
EOF
done
for i in petstore-openapi-v1-pets petstore-openapi-v1-users petstore-openapi-v2-full; do
cat <<EOF | kubectl apply -f -
apiVersion: portal.gloo.solo.io/v1beta1
kind: APIDoc
metadata:
name: $i
namespace: default
spec:
openApi:
content:
fetchUrl: https://raw.githubusercontent.com/solo-io/workshops/master/gloo-portal/openapi-specs/$i.json
EOF
done
cat << EOF | kubectl apply -f -
apiVersion: portal.gloo.solo.io/v1beta1
kind: APIProduct
metadata:
name: petstore-product
namespace: default
labels:
app: petstore
spec:
displayInfo:
title: Petstore Product
description: Fabulous API product for the Petstore
# ---------------- This API offers 2 usage plans ---------------------
usagePlans:
- basic2
# --------------------------------------------------------------------
versions:
- name: v1
apis:
- apiDoc:
name: petstore-openapi-v1-pets
namespace: default
- apiDoc:
name: petstore-openapi-v1-users
namespace: default
gatewayConfig:
route:
inlineRoute:
backends:
- upstream:
name: default-petstore-v1-8080
namespace: gloo-system
- name: v2
apis:
- apiDoc:
name: petstore-openapi-v2-full
namespace: default
gatewayConfig:
route:
inlineRoute:
backends:
- upstream:
name: default-petstore-v2-8080
namespace: gloo-system
EOF
####################
# ENVIRONMENT
####################
cat << EOF > env.yaml
apiVersion: portal.gloo.solo.io/v1beta1
kind: Environment
metadata:
name: dev
namespace: default
spec:
domains:
- ${API_FQDN} # the domain name where the API will be exposed
displayInfo:
description: This environment is meant for developers to deploy and test their APIs.
displayName: Development
basePath: /ecommerce # a global basepath for our APIs
apiProducts: # we will select our APIProduct using a selector and the 2 version of it
- namespaces:
- "*"
labels:
- key: app
operator: In
values:
- petstore
versions:
names:
- v1
- v2
basePath: "{%version%}" # this will dynamically prefix the API with the version names
# ------------------------ UPDATE -----------------------------
usagePlans:
- basic2
# -------------------------------------------------------------
gatewayConfig:
disableRoutes: false # we actually want to expose the APIs on a Gateway (optional)
parameters:
usagePlans:
# ------------------------- NEW --------------------------------
basic2:
authPolicy:
apiKey: {}
displayName: api-keys based plan
rateLimit:
requestsPerUnit: 5
unit: MINUTE
# --------------------------------------------------------------
EOF
kubectl apply -f env.yaml
###############
# TRY the APIs
###############
# GET one of the /pet endpoints, on the version 1
curl -s $(glooctl proxy url)/ecommerce/v1/api/pet/1 -H "Host: ${API_FQDN}" -v
# should return 401 because no api-key was provided
#############################
# Secure Portal with OIDC
#############################
KEYCLOAK_URL=http://$(kubectl get service keycloak -o jsonpath='{.status.loadBalancer.ingress[0].ip}'):8080/auth
KEYCLOAK_TOKEN=$(curl -d "client_id=admin-cli" -d "username=admin" -d "password=admin" -d "grant_type=password" "$KEYCLOAK_URL/realms/master/protocol/openid-connect/token" | jq -r .access_token)
KEYCLOAK_ID=$(curl -s -H "Authorization: bearer ${KEYCLOAK_TOKEN}" -H "Content-Type: application/json" "$KEYCLOAK_URL/admin/realms/master/clients" | jq -r '.[] | select(.redirectUris[0] == "https://'${PORTAL_FQDN}'/callback") | .id')
KEYCLOAK_CLIENT=$(curl -s -H "Authorization: bearer ${KEYCLOAK_TOKEN}" -H "Content-Type: application/json" "$KEYCLOAK_URL/admin/realms/master/clients" | jq -r '.[] | select(.redirectUris[0] == "https://'${PORTAL_FQDN}'/callback") | .clientId')
KEYCLOAK_SECRET=$(curl -s -H "Authorization: bearer ${KEYCLOAK_TOKEN}" -H "Content-Type: application/json" $KEYCLOAK_URL/admin/realms/master/clients/$KEYCLOAK_ID/client-secret | jq -r .value)
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
name: petstore-portal-oidc-secret
namespace: default
data:
client_secret: $(echo $KEYCLOAK_SECRET | base64)
EOF
cat <<EOF | kubectl apply -f -
apiVersion: portal.gloo.solo.io/v1beta1
kind: Portal
metadata:
name: ecommerce-portal
namespace: default
spec:
displayName: E-commerce Portal 2
description: The Gloo Portal for the Petstore API and much more!
banner:
fetchUrl: https://i.imgur.com/EXbBN1a.jpg
favicon:
fetchUrl: https://i.imgur.com/QQwlQG3.png
primaryLogo:
fetchUrl: https://i.imgur.com/hjgPMNP.png
customStyling: {}
staticPages: []
domains:
- ${PORTAL_FQDN}
publishedEnvironments:
- name: dev
namespace: default
allApisPublicViewable: true
# ------------------- NEW ---------------------
oidcAuth:
clientId: "${KEYCLOAK_CLIENT}"
clientSecret:
name: petstore-portal-oidc-secret
namespace: default
key: client_secret # this is the k8s secret we have created above
groupClaimKey: group # we will use the 'group' claim in the 'id_token' to associate the user with a group
issuer: ${KEYCLOAK_URL}/realms/master
# ---------------------------------------------
portalUrlPrefix: "http://${PORTAL_FQDN}/"
EOF
cat << EOF | kubectl apply -f -
apiVersion: portal.gloo.solo.io/v1beta1
kind: Group
metadata:
name: users
namespace: default
spec:
displayName: corporate users
accessLevel:
portals:
- name: ecommerce-portal
namespace: default
apis:
- environments:
names:
- dev
namespaces:
- '*'
# ------------------ Enforce basic auth usage plan ----------------
usagePlans:
- basic2
# -----------------------------------------------------------------
products:
namespaces:
- '*'
oidcGroup:
groupName: users # this represents the group name in the IdP (Keycloak)
EOF
Additional context
Add any other context about the problem here, e.g.
Describe the bug Given a Portal CR configured with OIDC, I am expecting a login button in the upper right corner of a Portal page to take me to the login page, where I can click "connect with some OIDC provider"
To Reproduce
Additional context Add any other context about the problem here, e.g.