frontier icon indicating copy to clipboard operation
frontier copied to clipboard

How to generate Bearer for API's

Open anishmenon opened this issue 1 year ago • 10 comments

i've deployed the frontier on digitalocean kubernetes with spiceDB i've followed the raystack official helm charts and modify its values to setup.

But how can i setup server side secret key for JWT via helm values. I didnt see anywhere in docs.

i've generated key using frontier server keygen locally. How can i do with configMaps or any other method. Also method to generate JWT for API communication.

eg : v1beta1/admin/users now i;m getting below error. as i'm not passing bearer token.

{"code":16, "message":"not authenticated", "details":[]}

Please guide

anishmenon avatar Sep 19 '23 12:09 anishmenon

@kushsharma

ravisuhag avatar Sep 19 '23 13:09 ravisuhag

@anishmenon JWT requires first setting up RSA keys for frontier, what you get using frontier server keygen are the private RSA keys. We must pass these keys using a config map to the frontier when it boots up. The reason it is not part of the chart is various people use various ways to pass it as a secure credential.

One easy way is to create a helm file templates/configmap-files.yaml within frontier charts directory and use

apiVersion: v1
kind: ConfigMap
metadata:
  name: "{{ .Chart.Name }}-files"
  namespace: "{{ include "app.namespace" . }}"
  annotations:
    "helm.sh/hook": pre-install,pre-upgrade
    "helm.sh/hook-weight": "-5"
    "helm.sh/resource-policy": "keep"
  labels:
    {{- include "app.labels" . | nindent 4 }}
data:
{{- if .Values.configPath }}
{{ (tpl (.Files.Glob (printf "%s" .Values.configPath) ).AsConfig . ) | indent 2 }}
{{- end }}

Create one more directory in frontier with the name files and add the generated key in a file called rsa and put it in this directory as ./files/rsa

In values.yaml add a line at the top, this will ensure our config map reads all the files from files directory and adds them as a key-value pair in the config map of kube.

configPath: "files/**"

So far we have put our RSA key in a config map but for frontier to find these keys we also need to mount it. Modify the frontier values.yaml as follows

container:
    command: ["frontier", "server", "start"]
    ports:
    - containerPort: 8080
      protocol: TCP
    - containerPort: 8081
      protocol: TCP
    volumeMounts:
    - name: configs
      mountPath: /etc/config
  volumes:
  - name: configs
    configMap:
      name: frontier-files
  config:
    FRONTIER_APP_AUTHENTICATION_TOKEN_RSA_PATH: /etc/config/rsa

Notice the newly added volumes and additional env config.

But before you do all this, I would advise you to setup everything in your local machine using docker and test how it works. Let me know if you need help in setting up locally.

kushsharma avatar Sep 19 '23 16:09 kushsharma

Here I want to use a presistance volume inorder to store the file or I need to build a docker image with the rsa key file and push to do docker hub.

To make it simple can I pass the raw json directly to configure map??

Like

key.json |

anishmenon avatar Sep 19 '23 17:09 anishmenon

The chart takes care of the volume for you, no need to build the docker image with rsa key. Your rsa key must be kept as secure as possible, never put it in docker, never put it in git repo.

There is another(easier way) to pass the RSA private keys using base64 encoded string, just ensure you don't check in this string in your version control. Run in shell to get base64 encoded keys

./frontier server keygen | base64

and put the string as

config:
    FRONTIER_APP_AUTHENTICATION_TOKEN_RSA_BASE64: "......long string......."

Ensure you use dev tag of the image as I just pushed this config a couple of minutes ago and it's not available on the latest release tag.

To verify if the RSA config is working, try hitting /.well-known/jwks.json endpoint to see your public keys.

kushsharma avatar Sep 19 '23 17:09 kushsharma

@kushsharma Thanks it is working.

Can you please explain the concept below example

  1. generate JWT using python /golang for Super admin with all permission scope to communicate with api
  2. generate JWT using python /golang for Organization SuperAdmin all permission scope of a spcefic org to communicate with api

anishmenon avatar Sep 20 '23 04:09 anishmenon

@anishmenon we only support user account(human) for superuser at the moment(no serviceuser support for superusers). For example if a user login using mail otp or google, it can be uniquely identified by its email id. This email id can be added in admin config or via env as FRONTIER_APP_ADMIN_USERS="[email protected]". When the frontier boots up, it automatically make this account a superuser. There is a workaround if you need temporary access to super user, you can exchange browser cookies with a jwt token via /v1beta1/auth/token endpoint. The endpoint expects sid cookie which is returned when we login/authenticate a user. This will give you a jwt, pass this jwt via Authorization: Bearer <jwt token> and the request will work as super user.

Organization-level admins serviceusers can be created as follows:

  1. Create a service user under an organization
  2. Assign app_organization_owner role to this service user by creating a policy
  3. Create a key credential of this service user
  4. Use the id of key credential as username and secret as password in header like Authorization: Basic <base64 encoded id:secret>

kushsharma avatar Sep 20 '23 17:09 kushsharma

private_key_dict = {"keys":[{"alg":"RS256","d":"o_9rp3y5gk_bj2edHFFBh3sgcJtKD750e5tidgjoj9RTe_in7USa_CGW2SXjM89zxNY4cOHT6RfC2OdwMRdNAfP7iPfIcheamhxbWf6VgIJjZKgUiJwb6pLxPQ9ghOXIq4sOpFtEUnQQg513mhufGhV-KvQp5-DcIU5zihY0Suta0jjjFyivOiAzw02-3sLlKDC8tmIVb9ABbHDCtUq2rzzxxzXtFnwPoJ8WD-COjlNwvTS0j-nUjvpK-5dNG2eB2HARWup7VIv8vFqmKOqrE8OgmmsgGPSnUx0OyVaj4c9QdPCqAMnAaG1P6C3ho2SQUT1nmR5QANeEQfhbEqp3WQ","dp":"Jfoy4ncFPXPO1Ay2aB1MY-UX_V1H35AwJM_qGmRLgWqIjGVNYKI0kaeQs8_xwILuzh22Gi_CwT7_EQ7fcJzK8YNxrw2uIisuqJ7Xu7dq8RLfc0T4Av9ChB31ZvN4jOwB_SjaNmtv8hw1Rg5xp5knXvGfs3gojh2lcpZJQ8rjYwc","dq":"PZRxzYYa1TLrXTdUa_FNEnmQNdsBx1DTEn7JYI6tjH7mb8R4GtsTOve2a6X_tNbwEStzfxApRaU8kmIECw3-1rgWCi7B1Bu0E2RFsnjvhV0Zzd9Y8Y4GNCQbxyWDfGqZ_YREGzSQMErid_4suoqatkXAfGv9ZEg2ii9T-9uPvsk","e":"AQAB","kid":"w_ruI4UbhZMyxjrIxSFKCteXfCSzrNWBemDUHAwvtV4","kty":"RSA","n":"yZHygO5LIfvoqb58jNF9rC90K9K_eSYd-npj5hIrloiRr7KCdJVG_1-yFN6BHCliMFtSwJ8wfacwU4zf6DjzhLFq8wTydw7kPlA7y3JdZX15_xXTppFosQVkB_kJT4gnEXXYGZboLitZXI0Ina8WgFsRlDnsuoHyZ3Aa7V2b6-XwgHG8M5CNpKgJlRZeTMooibWdNIL46mKAeuiuh6DliRHB6BW0VJyVtKkKwZBsCuDqVWDF9UIBO8P18CB2J9hYAVxSxlWyUxSFqfDFXTQOdmyayNdxuUe4nB_TskbNaZESzMQg-7f6xH5bUtIm8MsdnHbrs6TOYLjEpHIuTqADgw","p":"8GfgEacflpbkU6pOmnA5ZX37XNUUXw14EzCP3roksBuLjuT3tv7ExmnwmrV8gyttvX8tUoLUblOJNlLydibo1pd1ZFoJPoSberVbh2jVp5I5eCjU2a3-J5_mAnHhfxYnPwA0_zmMkAO-SCWT_jCv3J2HHIHEwrTX7E-Ez7Yxpmc","q":"1qUs_ESgmY2VF6VlpNqEOzyEVeBaOJ8r3DBqTSx_RGIuVErwOVZuzdpirU4CKaUrU4LowGkd5ayl_vIsIQgq-wCHGO1mukgqQBta2m38i7RJPOCWHEB65XsqVZPtnjbSY7Jb3uVjDSPYt6fV2x604ZbRAFxIqAm7ktQbDoIM8IU","qi":"Cq4ewNeymFibTVQLxSRpzKCPF699avAvM0djVZ9LG5wQcgO3Bay9klnRDm8nPUCgmbbnRFXI-cpAn98PSTC6U82ulq1t7SrCbUe2F-o580FGUaR9wwZlLGFO2KEw2uTMpi6TOFDvkek421t2D0L4x4xo_HWhxH96YZKAZWH2Mi8","use":"sig"},{"alg":"RS256","d":"M4BEQZaENKQDbXZaqa_E8njFN-s-ZqImI1H6uAlwOczcZOaWz6OOYgPY4VThu8u2HmNEtbof2zshnbMIrO6INasjfAavMl1wGkolmhuVhfdk6rvqAQIbOYcboZUTDwT6uOwlYh_fA49Khc0L43xRWrs6wUPTjDIfLkVV7dv3St8rKM-w94WCjbOcF73hXK2SkH8Lgjtak-uA-f4PPArnoFC96fKm1wPAMX7EncBIW3Yr81bXp4C9ObHR_s31CWjynpwt_8M3k9uNzUc5vyZYDPNe7BA35fCNzmODpGjPDP3bxFLUYVA9HKx7Y9u8KwO_rlaX-zKRrb-hE7xOrTQNOQ","dp":"LBuiHT3_4VS9Kgs2X1_pMkTOMVH7YSiUucDVITCuGP9ce9YQKXG-71Vsn8U93eGDOhqqNwzClNWKrIKzU12rV_-0E6wJ4el5oPpXUsLlv_9t3WrW1byiDPQ7M9oeZOTLHLSKg8XZ1qSnWwkwPqXrjsrlZpFubNqcF0kh3eXiEeU","dq":"DlVBfArJjb8ephF3lMZjh5KDA_N4sLTjVRS2maoJqkm_BBPWDBGIRfxvGcdRLPMZR4_DKeJlB5g9Xp4N-g4k25FuDKCwc_1dySaYVU908QuUQQ04AqVrTC0qvrywOlNGxxLpgbZS2FWYIkTQsRbPDg7C5G9Q8Fl5n5G14MWbxhc","e":"AQAB","kid":"Y6dFyZbPWwpoOPV77AdREAnLZDWpQhcZpAdmNQ9fMi4","kty":"RSA","n":"xPSRhzCAhIl-kMey6xPkfKwGwzFJZAVjrR-QxMV9dOEvVhra4aK5h2udbIYxZYc8hGASD30aap2NrHGv5ktG4skeU9ZHwANiERfii5KV2w5W0vkYMvi6fGmK3PM1E-I1emdRnNt-FjuirlM7elV9g3O4bCISX-ZbOE-JBKyWBjpnaOi551mPClIj_pfWSQHKxupIsKPPNGpIpoGCTGukPuzH-sUI6QCEZABQJGLuzHf6WW16hLp71Av_YaagG76BDmF2zj2y5DV0-QWeBWPGZtfqOq4lkAq2OOeyjqLMF56o8JDcCeDqH8rGpwi-f-GGqx0ouIyGh8YiAEhQk-Zbnw","p":"1IE30ODgA2YuAmDaM_JisTQLVOXCmFLD9CKukaiNQI8hY6itlKcMRZAsqXwbYm63aAzg4t38h5O93qPp6ETHhE0V6a6xIIbQOeNJjE14XeSr8yQLOikimd9ewdOqipu2LlOoAam5gwxwiLbV5eG-CF1FwNuaaxoLg_c21xnM_s0","q":"7USYbfG_gEUV3kDmFfvAf6WjsQN39gPAwf3H5aipcAXGTzPNjo3kbCl0V5SXljq6h7rbkm7DBbUuzn7oJYMhaXvN9GiQoUHgel3bZQ3rNgbwMsp2lTRwGWC6_6PaboqUOtK1e_KWD2sMFUVQ4xA9Wzoxes7hZJPRQGz8WGztbBs","qi":"qWCN84ko3EVjkS8gc2UGfVd_0F7VsimTyffKuzki8Mi1kmx-iUdo0vavioOqMhB2CbDPWCKoDzp4Ia3K8f9ufqhGv96rWtEnvGK2ER52UmhwsP5a94aPq0khsDJyr5dyRvBhAbW7IjnBGGNdosN-t2XRqxtGOna-d_JWho2VmXM","use":"sig"}]}

prv_first_key_dict = private_key_dict['keys'][0]
your_rsa_private_key = dict_to_private_key(prv_first_key_dict)

# Create Payload

payload = {
    "sub": "1234567890",
    "name": "John Doe",
    "email": "[email protected]", // which is added in admin users
    "iat": 1516239022,
    "iss": "https://example.com",
    
}

encoded_jwt = jwt.encode(payload, your_rsa_private_key, algorithm='RS256')
print(encoded_jwt)

I tried pyJWT and generated JWT i tried communicating with below api give authentication 401Unauthorized error

`curl -L -X POST 'https://myservice.com/v1beta1/organizations'
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer JWT GENERATED'
--data-raw '{ "name": "DEMOOO", "title": "Demo Org", "metadata": {}, "avatar": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAA" }'

@kushsharma

anishmenon avatar Sep 20 '23 18:09 anishmenon

You don't create jwt using the private keys, they are only for frontier, otherwise all of your microservices that wants to communicate would require these private keys. Private keys should be kept as safe as possible.

To create a jwt, frontier provides an endpoint /v1beta1/auth/token. Go through the authn part of docs for more information. Essentially follow these steps one to one maybe via postman/curl if that makes it easy. You can import swagger openapi spec from proto directory.

  1. Create a service user under an organization
  2. Assign app_organization_owner role to this service user by creating a policy
  3. Create a key credential of this service user
  4. Use the id of key credential as username and secret as password in header like Authorization: Basic

There is also an example provided for browser based logins under examples/authn/main.go. Run it via go run main.go -frontierhost http://localhost:7400 once you have configured oidc or mailer configuration of frontier.

kushsharma avatar Sep 21 '23 03:09 kushsharma

Hi @kushsharma i am having difficulty while accessing the admin portal in ui and as well via api. so i am using docker container with below runtime config

frontier: image: raystack/frontier:latest ports: - "8081:8080" command: server start restart: on-failure volumes: - ./rsa:/opt/rsa depends_on: pg: condition: service_healthy environment: - FRONTIER_DB_DRIVER=postgres - FRONTIER_DB_URL=postgres://frontier:@pg:5432/frontier?sslmode=disable - FRONTIER_SPICEDB_PORT=50051 - FRONTIER_SPICEDB_HOST=spicedb - FRONTIER_SPICEDB_PRE_SHARED_KEY=frontier - FRONTIER_APP_RESOURCES_CONFIG_PATH=file:///opt - FRONTIER_APP_ADMIN_USERS="[email protected]" - FRONTIER_APP_AUTHENTICATION_TOKEN_RSA_PATH=/opt/rsa I have generated RSA: frontier server keygen Also i am using [email protected] to login i can see logout button but unauthorised acces Screenshot 2024-02-20 at 9 04 33 AM

I have used localhost:8081/v1beta1/auth/token api by sending sid cookie (grab from ui ) and i was successfully able to get the jwt token.

next step i have used admin api its not getting authenticated. let me know if i am doing something wrong here. Also consider i didn't seed anything into database

surendratiwari3 avatar Feb 20 '24 03:02 surendratiwari3

its working i just needed to remove quote ;-)

surendratiwari3 avatar Feb 20 '24 04:02 surendratiwari3