Closed NamalSanjaya closed 1 year ago
Each request comes to our servers need to have enough permission to serve by our servers. We need one place that have ability to check authenticity
and required permission
. Therefore all other servers need to rely on Identity server to verify its identity and whether it has required permission to access the resource.
Need to learn
Concepts we need
{
"x5t": "YWY1NmRkZGI5MzE4ODBlZDRiMWI4ZWJlNjExZWViZjEzMmNmNDU3YQ",
"kid": "MWQ5NWUwYWZiMmMzZTIzMzdmMzBhMWM4YjQyMjVhNWM4NjhkMGRmNzFlMGI3ZDlmYmQzNmEyMzhhYjBiNmZhYw_RS256",
"alg": "RS256"
}
{
"sub": "6ded9f12-352d-4ad9-9cc0-c4803a8655bc",
"aut": "APPLICATION_USER",
"iss": "https://sts.choreo.dev:443/oauth2/token",
"aud": [
"ciwnWuwZfbcdzBUcnkhKvi_mcBUa",
"https://sts.preview.choreo.dev:443/oauth2/token",
"https://apim.preview.choreo.dev:443/oauth2/token",
"https://sts.choreo.dev:443/oauth2/token"
],
"nbf": 1685853490,
"azp": "ciwnWuwZfbcdzBUcnkhKvi_mcBUa",
"scope": "apim:admin apim:api_generate_key apim:api_manage apim:api_publish apim:api_settings apim:api_view apim:dcr:app_manage apim:document_manage apim:environment_manage apim:publisher_settings apim:subscribe apim:subscription_manage apim:subscription_view apim:tier_manage apim:tier_view choreo:component_manage choreo:deployment_manage choreo:dev_env_manage choreo:log_view_non_prod choreo:log_view_prod choreo:non_prod_env_manage choreo:prod_env_manage choreo:project_manage environments:view_dev environments:view_prod urn:choreosystem:choreodevopsportalapi:component_manage urn:choreosystem:choreodevopsportalapi:deployment_manage urn:choreosystem:choreodevopsportalapi:deployment_view urn:choreosystem:componentsmanagement:component_config_view urn:choreosystem:componentsmanagement:component_create urn:choreosystem:componentsmanagement:component_file_view urn:choreosystem:componentsmanagement:component_init_view urn:choreosystem:componentsmanagement:component_logs_view urn:choreosystem:componentsmanagement:component_manage urn:choreosystem:componentsmanagement:component_trigger urn:choreosystem:configmanagement:config_create urn:choreosystem:configmanagement:config_delete urn:choreosystem:configmanagement:config_manage urn:choreosystem:configmanagement:config_view urn:choreosystem:configmanagement:global_config_create urn:choreosystem:configmanagement:global_config_delete urn:choreosystem:configmanagement:global_config_manage urn:choreosystem:configmanagement:global_config_update urn:choreosystem:configmanagement:global_config_view urn:choreosystem:customdomainapi:custom_domain_create urn:choreosystem:customdomainapi:custom_domain_delete urn:choreosystem:customdomainapi:custom_domain_manage urn:choreosystem:customdomainapi:custom_domain_update urn:choreosystem:customdomainapi:custom_domain_view urn:choreosystem:onpremkeymanagement:on_prem_key_create urn:choreosystem:onpremkeymanagement:on_prem_key_delete urn:choreosystem:onpremkeymanagement:on_prem_key_manage urn:choreosystem:onpremkeymanagement:on_prem_key_update urn:choreosystem:onpremkeymanagement:on_prem_key_view urn:choreosystem:organizationmanagement:enterprise_login_config_manage urn:choreosystem:organizationmanagement:enterprise_login_config_view urn:choreosystem:organizationmanagement:self_signup_approval_update urn:choreosystem:organizationmanagement:self_signup_approval_view urn:choreosystem:organizationmanagement:self_signup_config_update urn:choreosystem:organizationmanagement:self_signup_config_view urn:choreosystem:organizationmanagement:self_signup_manage urn:choreosystem:organizationmanagement:theme_create urn:choreosystem:organizationmanagement:theme_delete urn:choreosystem:organizationmanagement:theme_deploy urn:choreosystem:organizationmanagement:theme_manage urn:choreosystem:organizationmanagement:theme_view urn:choreosystem:usersmanagement:invitation_delete urn:choreosystem:usersmanagement:invitation_manage urn:choreosystem:usersmanagement:invitation_send urn:choreosystem:usersmanagement:invitation_view urn:choreosystem:usersmanagement:permission_view urn:choreosystem:usersmanagement:role_create urn:choreosystem:usersmanagement:role_delete urn:choreosystem:usersmanagement:role_manage urn:choreosystem:usersmanagement:role_mapping_create urn:choreosystem:usersmanagement:role_mapping_delete urn:choreosystem:usersmanagement:role_mapping_manage urn:choreosystem:usersmanagement:role_mapping_update urn:choreosystem:usersmanagement:role_mapping_view urn:choreosystem:usersmanagement:role_update urn:choreosystem:usersmanagement:role_view urn:choreosystem:usersmanagement:user_delete urn:choreosystem:usersmanagement:user_manage urn:choreosystem:usersmanagement:user_update urn:choreosystem:usersmanagement:user_view",
"organization": {
"handle": "namalbcsnv",
"uuid": "c1b8ad67-c553-4253-8095-13f4467d3f4a"
},
"organizations": [
"c1b8ad67-c553-4253-8095-13f4467d3f4a"
],
"exp": 1685857090,
"idp_claims": {
"aut": "APPLICATION_USER",
"authenticated_idp": "Google",
"name": "Namal Sanjaya",
"given_name": "Namal",
"family_name": "Sanjaya",
"email": "namalsanjaya23@gmail.com"
},
"iat": 1685853490,
"jti": "7d825bbf-ed93-4d27-8b28-c4245629e40b"
}
We are sign JWT token using our private key(we have digital signature). Therefore other only can see this read this. they can't modify the content. Check more advatanges of using JWT token as an access token.
I decide to use multi-model database which is arango db
.
JWT Registered claims
{
"iss" : "https://mora.nexster.com/auth",
"aud" : [
"https://mora.nexster.com/auth", "https://mora.nexster.com/timeline"
]
"sub" : "uuid of user",
"exp" : "194783291",
"username" : "Namal Sanjaya",
"img_url" : "https://api.profimage.com/users/34"
}
package main
import (
"fmt"
"log"
"os"
"time"
jwt "github.com/golang-jwt/jwt/v5"
)
func main() {
stTime := time.Now()
s := genJwt()
fmt.Println(s)
fmt.Println("---- End JWT ----")
decodeJwt(s)
fmt.Println("--done--, Ex: ", time.Now().Sub(stTime))
}
func genJwt() string {
privateKey, err := os.ReadFile("private_key_pkcs8.pem")
if err != nil {
log.Fatal(err)
}
key, err := jwt.ParseECPrivateKeyFromPEM(privateKey)
if err != nil {
log.Fatal(err)
}
t := jwt.NewWithClaims(jwt.SigningMethodES256,
jwt.MapClaims{
"iss": "my-auth-server",
"sub": "john",
"foo": 2,
})
s, err := t.SignedString(key)
if err != nil {
log.Fatal(err)
}
return s
}
func decodeJwt(tokenString string) {
key, err := os.ReadFile("public_key.pem")
if err != nil {
log.Fatal("Error file open: ", err)
}
publicKey, err := jwt.ParseECPublicKeyFromPEM(key)
if err != nil {
log.Fatal("Error ParseECPublic open: ", err)
}
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
return publicKey, nil // In this example, we use a constant public key
})
if err != nil {
log.Fatal("Error parsing token:", err)
}
// Verify the token's signature
if !token.Valid {
log.Fatal("Invalid token signature")
}
// Access the claims contained within the token's payload
claims, ok := token.Claims.(jwt.MapClaims)
if !ok {
log.Fatal("Invalid token claims")
}
// Access individual claim values
issuer := claims["iss"].(string)
subject := claims["sub"].(string)
foo := int(claims["foo"].(float64)) // Cast to appropriate types
// Print the decoded claims
fmt.Println("Issuer:", issuer)
fmt.Println("Subject:", subject)
fmt.Println("Foo:", foo)
}
httpOnly
header. (Then cookies are not accessible with JS)secure
flag to restrict only to be used with httpssameSite=value
, this is to prevent from CSRF attack. (value = None | Lax | Strict )
Lax
- with such configuration browsers will not send cookies for images and frames, so it will prevent CSRF attacks
Strict
- in that case cross-site cookies are completely blockedSet-Cookie: jwt=OUR_TOKEN_CONTENT; secure; httpOnly; sameSite=Lax;
https://mostafa-asg.github.io/post/nginx-authentication-with-jwt/ https://github.com/TeslaGov/ngx-http-auth-jwt-module
I have 2 architectures in my head
We Go with 2nd option since I am unable to figure out a way to do JWT validation in Nginx open source. (in docker conainer it is possible I think. But we are not going in this path)
I have chosen cookie to store Jwt token
. Browser won't automatically add token
to API fetch request. Therefore, we have to remove httpOnly
cookie attribute which is there to avoid XSS
attacks. Now we are vularable to XSS
attacks.
This is the current architecture.
I could not figure out a way to do JWT token validation at NGINX. I could direct very request to another auth server and let the auth server to do the JWT validation, but it can create a performance bottleneck. There fore, I have to do JWT validation at each server. Not public Key is spread across multiple places.
In future, we need to figure out a better way to that.
Use localStorage
to store user information
Store user info
// Create the JavaScript object
const userData = {
gender: "male",
birthday: "2002-03-14",
faculty: "engineering",
userid: "482191"
};
// Store the JSON string in localStorage under a key (e.g., "user_data")
localStorage.setItem("user_data", JSON.stringify(userData));
Retrieve user info
// Retrieve the JSON string from localStorage
const jsonString = localStorage.getItem("user_data");
// Parse the JSON string back into a JavaScript object
const userData = JSON.parse(jsonString);
console.log(userData); // This will output the JavaScript object
Identity Server need to have following capabilities
This list can have more to add.