Repository: sebadob/rauthy (language: rust)

Issue:
Non ASCII characters not supported in some places
My given name is "Lukáš", but I cannot use it in Rauthy, because only ASCII strings are supported it seems.

![image](https://github.com/user-attachments/assets/ffed58b8-65f7-4534-966c-40ddf483d15a)



Discussion / hints:
Oh, that's a frontend validation bug, because the backend allows them:

`[a-zA-Z0-9À-ÿ-\s]{1,32}`

In which module in the UI do you get this error?
In Account/Edit and Admin/Users
Actually, it's not a bug, the `š` is the issue as it seems to be not covered by `À-ÿ`.

I don't know about your alphabet, but if you could give me a list with the unicode variants of all missing characters, I could add them.

The current regex used in the frontend is:

```js
/^[\w\sÀ-ÿ\-\u3041-\u3096\u30A0-\u30FF\u3400-\u4DB5\u4E00-\u9FCB\uF900-\uFA6A\u2E80-\u2FD5\uFF66-\uFF9F\uFFA1-\uFFDC\u31F0-\u31FF]{1,32}$/gm
```

And the backend uses a matching one, just in rust.
I don't think this is the right way to do. What about chinese and Japanese people etc?

What is the reason to even limit human readable name to regex?

I understand this for system values, but it does not make sense here I think.
The same should apply to Client human readable name etc.
> I don't think this is the right way to do. What about chinese and Japanese people etc?

Most asian characters are included in the regex above.

> What is the reason to even limit human readable name to regex?

The values are almost always displayed to the user in some form. Rauthy itself never inserts data into the DOM as HTML, but client applications may do this, when they grab the data from tokens. Not a single value that you can submit to Rauthy's API will be unvalidated or just taken as it is, except from a password, which will be hashed right away anyway.

> I understand this for system values, but it does not make sense here I think.

It makes sense in the way that it will prevent people from choosing absolute nonsense names, or simply an Emoji as their name. Usually, `À-ÿ` should cover all special characters in the western alphabet, I didn't even know about the `š`.

> The same should apply to Client human readable name etc.

It does already, the client name regex is very similar to the user names.
Ou, it makes sense.

Uppercase Czech letters with diacritics:

```
\u00C1 (Á), \u010C (Č), \u010E (Ď), \u00C9 (É), \u011A (Ě), \u00CD (Í), \u0147 (Ň), \u00D3 (Ó), \u0158 (Ř), \u0160 (Š), \u0164 (Ť), \u00DA (Ú), \u016E (Ů), \u00DD (Ý), \u017D (Ž)
```

Lowercase Czech letters with diacritics:

```
\u00E1 (á), \u010D (č), \u010F (ď), \u00E9 (é), \u011B (ě), \u00ED (í), \u0148 (ň), \u00F3 (ó), \u0159 (ř), \u0161 (š), \u0165 (ť), \u00FA (ú), \u016F (ů), \u00FD (ý), \u017E (ž)
```
Okay, I checked this and the default JS `À-ÿ` actually ends right before quite a few characters you need, but it also includes some already. At first glance, I guess it would be fine for you if I include `Latin Extended-A` like [shown here](https://symbl.cc/en/unicode/blocks/latin-extended-a/)?

This could be done very simply by adding the range `\u0100-\u017F`.

Edit:

Maybe it would even work if I change the regex from `À-ÿ` to `À-ſ`, never tested this before.

It seems it would work :)

Fix patch:
diff --git a/book/src/config/user_reg.md b/book/src/config/user_reg.md
index 5f372b649..7628a2e9c 100644
--- a/book/src/config/user_reg.md
+++ b/book/src/config/user_reg.md
@@ -158,10 +158,10 @@ struct NewUserRegistrationRequest {
     #[validate(email)]
     email: String,
     /// Validation: `[a-zA-Z0-9À-ÿ-\\s]{1,32}`
-    #[validate(regex(path = "*RE_USER_NAME", code = "[a-zA-Z0-9À-ÿ-\\s]{1,32}"))]
+    #[validate(regex(path = "*RE_USER_NAME", code = "[a-zA-Z0-9À-ſ-\\s]{1,32}"))]
     family_name: String,
     /// Validation: `[a-zA-Z0-9À-ÿ-\\s]{1,32}`
-    #[validate(regex(path = "*RE_USER_NAME", code = "[a-zA-Z0-9À-ÿ-\\s]{1,32}"))]
+    #[validate(regex(path = "*RE_USER_NAME", code = "[a-zA-Z0-9À-ſ-\\s]{1,32}"))]
     given_name: String,
     /// Validation: `[a-zA-Z0-9,.:/_\-&?=~#!$'()*+%]+`
     #[validate(regex(path = "*RE_URI", code = "[a-zA-Z0-9,.:/_\\-&?=~#!$'()*+%]+"))]
diff --git a/frontend/src/utils/constants.js b/frontend/src/utils/constants.js
index b79cd1b0c..923a52be1 100644
--- a/frontend/src/utils/constants.js
+++ b/frontend/src/utils/constants.js
@@ -19,7 +19,7 @@ export const REGEX_STREET = /^[a-zA-Z0-9À-ÿ-.\s]{0,48}$/gm;
 export const REGEX_CITY = /^[a-zA-Z0-9À-ÿ-]{0,48}$/gm;
 export const REGEX_API_KEY = /^[a-zA-Z0-9_/-]{2,24}$/gm;
 // This regex includes:
-//   ASCII alphabet, numeric characters
+//   ASCII alphabet, numeric characters, latin-1 supplement + latin-1 extended A
 //   single white space characters
 //   hyphen-minus '-'
 //   Unicode BMP:
@@ -30,11 +30,11 @@ export const REGEX_API_KEY = /^[a-zA-Z0-9_/-]{2,24}$/gm;
 //         Kangxi Radicals
 //     - a part of Halfwidth and Fullwidth Forms
 //   Unicode SMP and SIP planes are not included due to JavaScript regex limitations
-export const REGEX_NAME = /^[\w\sÀ-ÿ\-\u3041-\u3096\u30A0-\u30FF\u3400-\u4DB5\u4E00-\u9FCB\uF900-\uFA6A\u2E80-\u2FD5\uFF66-\uFF9F\uFFA1-\uFFDC\u31F0-\u31FF]{1,32}$/gm;
+export const REGEX_NAME = /^[\w\sÀ-ſ\-\u3041-\u3096\u30A0-\u30FF\u3400-\u4DB5\u4E00-\u9FCB\uF900-\uFA6A\u2E80-\u2FD5\uFF66-\uFF9F\uFFA1-\uFFDC\u31F0-\u31FF]{1,32}$/gm;
 export const REGEX_ATTR_DESC = /^[a-zA-Z0-9\-_/\s]{0,128}$/gm;
 export const REGEX_ATTR_KEY = /^[a-zA-Z0-9\-_/]{2,32}$/gm;
 export const REGEX_CLIENT_ID = /^[a-zA-Z0-9\-_/]{2,128}$/gm;
-export const REGEX_CLIENT_NAME = /^[a-zA-Z0-9À-ÿ\-\s\u3041-\u3096\u30A0-\u30FF\u3400-\u4DB5\u4E00-\u9FCB\uF900-\uFA6A\u2E80-\u2FD5\uFF66-\uFF9F\uFFA1-\uFFDC\u31F0-\u31FF]{0,128}$/m;
+export const REGEX_CLIENT_NAME = /^[a-zA-Z0-9À-ſ\-\s\u3041-\u3096\u30A0-\u30FF\u3400-\u4DB5\u4E00-\u9FCB\uF900-\uFA6A\u2E80-\u2FD5\uFF66-\uFF9F\uFFA1-\uFFDC\u31F0-\u31FF]{0,128}$/m;
 export const REGEX_CONTACT = /^[a-zA-Z0-9+.@/:]{0,48}$/gm;
 export const REGEX_LOWERCASE_SPACE = /^[a-z0-9-_\/\s]{2,128}$/gm;
 export const REGEX_PROVIDER_SCOPE = /^[a-z0-9-_\/:\s]{0,128}$/gm;
diff --git a/frontend/vite.config.js b/frontend/vite.config.js
index 600ab1e78..eac92451a 100644
--- a/frontend/vite.config.js
+++ b/frontend/vite.config.js
@@ -12,10 +12,10 @@ const config = {
         // If you want to run with dev TLS certificates, for instance when you use a remote host, uncomment the following
         // lines. Do not forget to adjust the `PUB_URL` in `rauthy.cfg` accordingly to allow the `redirect_uri`, for instance:
         // `PUB_URL=your_remote_host_ip:8443`
-        https: {
-            key: fs.readFileSync(`${__dirname}/../tls/key.pem`),
-            cert: fs.readFileSync(`${__dirname}/../tls/cert-chain.pem`)
-        },
+        // https: {
+        //     key: fs.readFileSync(`${__dirname}/../tls/key.pem`),
+        //     cert: fs.readFileSync(`${__dirname}/../tls/cert-chain.pem`)
+        // },
         proxy: {
             '/.well-known/': backend,
             '/auth/v1/_app': backend,
diff --git a/src/api_types/src/auth_providers.rs b/src/api_types/src/auth_providers.rs
index 8d54fff05..698b4c4db 100644
--- a/src/api_types/src/auth_providers.rs
+++ b/src/api_types/src/auth_providers.rs
@@ -19,7 +19,7 @@ pub enum AuthProviderType {
 #[derive(Debug, Deserialize, Validate, ToSchema)]
 pub struct ProviderRequest {
     /// Validation: `[a-zA-Z0-9À-ÿ-\s]{2,128}]`
-    #[validate(regex(path = "*RE_CLIENT_NAME", code = "[a-zA-Z0-9À-ÿ-\\s]{2,128}"))]
+    #[validate(regex(path = "*RE_CLIENT_NAME", code = "[a-zA-Z0-9À-ſ-\\s]{2,128}"))]
     pub name: String,
     pub typ: AuthProviderType,
     pub enabled: bool,
diff --git a/src/api_types/src/clients.rs b/src/api_types/src/clients.rs
index 81a46ed2e..6dbf81eed 100644
--- a/src/api_types/src/clients.rs
+++ b/src/api_types/src/clients.rs
@@ -70,7 +70,7 @@ pub struct DynamicClientRequest {
     #[validate(custom(function = "validate_vec_grant_types"))]
     pub grant_types: Vec<String>,
     /// Validation: `[a-zA-Z0-9À-ÿ-\\s]{2,128}`
-    #[validate(regex(path = "*RE_CLIENT_NAME", code = "[a-zA-Z0-9À-ÿ-\\s]{2,128}"))]
+    #[validate(regex(path = "*RE_CLIENT_NAME", code = "[a-zA-Z0-9À-ſ-\\s]{2,128}"))]
     pub client_name: Option<String>,
     /// Validation: `[a-zA-Z0-9,.:/_-&?=~#!$'()*+%]+$`
     #[validate(regex(path = "*RE_URI", code = "[a-zA-Z0-9,.:/_-&?=~#!$'()*+%]+$"))]
@@ -133,7 +133,7 @@ pub struct EphemeralClientRequest {
     ))]
     pub client_id: String,
     /// Validation: `[a-zA-Z0-9À-ÿ-\\s]{2,128}`
-    #[validate(regex(path = "*RE_CLIENT_NAME", code = "[a-zA-Z0-9À-ÿ-\\s]{2,128}"))]
+    #[validate(regex(path = "*RE_CLIENT_NAME", code = "[a-zA-Z0-9À-ſ-\\s]{2,128}"))]
     pub client_name: Option<String>,
     /// Validation: `[a-zA-Z0-9,.:/_-&?=~#!$'()*+%]+$`
     #[validate(regex(path = "*RE_URI", code = "[a-zA-Z0-9,.:/_-&?=~#!$'()*+%]+$"))]
@@ -173,7 +173,7 @@ pub struct NewClientRequest {
     #[serde(skip_deserializing)]
     pub secret: Option<Vec<u8>>,
     /// Validation: `[a-zA-Z0-9À-ÿ-\\s]{2,128}`
-    #[validate(regex(path = "*RE_CLIENT_NAME", code = "[a-zA-Z0-9À-ÿ-\\s]{2,128}"))]
+    #[validate(regex(path = "*RE_CLIENT_NAME", code = "[a-zA-Z0-9À-ſ-\\s]{2,128}"))]
     pub name: Option<String>,
     /// Validation: bool
     pub confidential: bool,
@@ -194,7 +194,7 @@ pub struct UpdateClientRequest {
     ))]
     pub id: String,
     /// Validation: `[a-zA-Z0-9À-ÿ-\\s]{2,128}`
-    #[validate(regex(path = "*RE_CLIENT_NAME", code = "[a-zA-Z0-9À-ÿ-\\s]{2,128}"))]
+    #[validate(regex(path = "*RE_CLIENT_NAME", code = "[a-zA-Z0-9À-ſ-\\s]{2,128}"))]
     pub name: Option<String>,
     pub confidential: bool,
     /// Validation: `Vec<^[a-zA-Z0-9,.:/_\\-&?=~#!$'()*+%]+$>`
diff --git a/src/api_types/src/users.rs b/src/api_types/src/users.rs
index 73aaecea4..e9402671d 100644
--- a/src/api_types/src/users.rs
+++ b/src/api_types/src/users.rs
@@ -15,7 +15,7 @@ pub struct DeviceRequest {
     #[validate(regex(path = "*RE_URI", code = "[a-zA-Z0-9,.:/_\\-&?=~#!$'()*+%]+$"))]
     pub device_id: String,
     /// Validation: `[a-zA-Z0-9À-ÿ-\\s]{2,128}`
-    #[validate(regex(path = "*RE_CLIENT_NAME", code = "[a-zA-Z0-9À-ÿ-\\s]{2,128}"))]
+    #[validate(regex(path = "*RE_CLIENT_NAME", code = "[a-zA-Z0-9À-ſ-\\s]{2,128}"))]
     pub name: Option<String>,
 }
 
@@ -43,10 +43,10 @@ pub struct NewUserRequest {
     #[validate(email)]
     pub email: String,
     /// Validation: `[a-zA-Z0-9À-ÿ-\\s]{1,32}`
-    #[validate(regex(path = "*RE_USER_NAME", code = "[a-zA-Z0-9À-ÿ-\\s]{1,32}"))]
+    #[validate(regex(path = "*RE_USER_NAME", code = "[a-zA-Z0-9À-ſ-\\s]{1,32}"))]
     pub family_name: String,
     /// Validation: `[a-zA-Z0-9À-ÿ-\\s]{1,32}`
-    #[validate(regex(path = "*RE_USER_NAME", code = "[a-zA-Z0-9À-ÿ-\\s]{1,32}"))]
+    #[validate(regex(path = "*RE_USER_NAME", code = "[a-zA-Z0-9À-ſ-\\s]{1,32}"))]
     pub given_name: String,
     pub language: Language,
     /// Validation: `Vec<^[a-z0-9-_/,:*]{2,64}$>`
@@ -67,10 +67,10 @@ pub struct NewUserRegistrationRequest {
     #[validate(email)]
     pub email: String,
     /// Validation: `[a-zA-Z0-9À-ÿ-\\s]{1,32}`
-    #[validate(regex(path = "*RE_USER_NAME", code = "[a-zA-Z0-9À-ÿ-\\s]{1,32}"))]
+    #[validate(regex(path = "*RE_USER_NAME", code = "[a-zA-Z0-9À-ſ-\\s]{1,32}"))]
     pub family_name: String,
     /// Validation: `[a-zA-Z0-9À-ÿ-\\s]{1,32}`
-    #[validate(regex(path = "*RE_USER_NAME", code = "[a-zA-Z0-9À-ÿ-\\s]{1,32}"))]
+    #[validate(regex(path = "*RE_USER_NAME", code = "[a-zA-Z0-9À-ſ-\\s]{1,32}"))]
     pub given_name: String,
     /// Validation: `[a-zA-Z0-9,.:/_\-&?=~#!$'()*+%]+`
     #[validate(regex(path = "*RE_URI", code = "[a-zA-Z0-9,.:/_\\-&?=~#!$'()*+%]+"))]
@@ -83,7 +83,7 @@ pub struct NewUserRegistrationRequest {
 #[derive(Debug, Serialize, Deserialize, Validate, ToSchema)]
 pub struct PasskeyRequest {
     /// Validation: `[a-zA-Z0-9À-ÿ-\\s]{1,32}`
-    #[validate(regex(path = "*RE_USER_NAME", code = "[a-zA-Z0-9À-ÿ-\\s]{1,32}"))]
+    #[validate(regex(path = "*RE_USER_NAME", code = "[a-zA-Z0-9À-ſ-\\s]{1,32}"))]
     pub name: String,
 }
 
@@ -116,10 +116,10 @@ pub struct UpdateUserRequest {
     #[validate(email)]
     pub email: String,
     /// Validation: `[a-zA-Z0-9À-ÿ-\\s]{2,128}`
-    #[validate(regex(path = "*RE_CLIENT_NAME", code = "[a-zA-Z0-9À-ÿ-\\s]{2,128}"))]
+    #[validate(regex(path = "*RE_CLIENT_NAME", code = "[a-zA-Z0-9À-ſ-\\s]{2,128}"))]
     pub given_name: String,
     /// Validation: `[a-zA-Z0-9À-ÿ-\\s]{2,128}`
-    #[validate(regex(path = "*RE_CLIENT_NAME", code = "[a-zA-Z0-9À-ÿ-\\s]{2,128}"))]
+    #[validate(regex(path = "*RE_CLIENT_NAME", code = "[a-zA-Z0-9À-ſ-\\s]{2,128}"))]
     pub family_name: String,
     pub language: Option<Language>,
     /// Validation: Applies password policy - max 256 characters
@@ -148,10 +148,10 @@ pub struct UpdateUserSelfRequest {
     #[validate(email)]
     pub email: Option<String>,
     /// Validation: `[a-zA-Z0-9À-ÿ-\\s]{2,128}`
-    #[validate(regex(path = "*RE_CLIENT_NAME", code = "[a-zA-Z0-9À-ÿ-\\s]{2,128}"))]
+    #[validate(regex(path = "*RE_CLIENT_NAME", code = "[a-zA-Z0-9À-ſ-\\s]{2,128}"))]
     pub given_name: Option<String>,
     /// Validation: `[a-zA-Z0-9À-ÿ-\\s]{2,128}`
-    #[validate(regex(path = "*RE_CLIENT_NAME", code = "[a-zA-Z0-9À-ÿ-\\s]{2,128}"))]
+    #[validate(regex(path = "*RE_CLIENT_NAME", code = "[a-zA-Z0-9À-ſ-\\s]{2,128}"))]
     pub family_name: Option<String>,
     pub language: Option<Language>,
     pub password_current: Option<String>,
@@ -225,7 +225,7 @@ pub struct WebauthnAuthFinishRequest {
 #[derive(Debug, Serialize, Deserialize, Validate, ToSchema)]
 pub struct WebauthnRegStartRequest {
     /// Validation: `[a-zA-Z0-9À-ÿ-\\s]{1,32}`
-    #[validate(regex(path = "*RE_USER_NAME", code = "[a-zA-Z0-9À-ÿ-\\s]{1,32}"))]
+    #[validate(regex(path = "*RE_USER_NAME", code = "[a-zA-Z0-9À-ſ-\\s]{1,32}"))]
     pub passkey_name: String,
     /// Validation: `[a-zA-Z0-9]{64}`
     #[validate(regex(path = "*RE_ALNUM_64", code = "[a-zA-Z0-9]{64}"))]
@@ -235,7 +235,7 @@ pub struct WebauthnRegStartRequest {
 #[derive(Debug, Serialize, Deserialize, Validate, ToSchema)]
 pub struct WebauthnRegFinishRequest {
     /// Validation: `[a-zA-Z0-9À-ÿ-\\s]{1,32}`
-    #[validate(regex(path = "*RE_USER_NAME", code = "[a-zA-Z0-9À-ÿ-\\s]{1,32}"))]
+    #[validate(regex(path = "*RE_USER_NAME", code = "[a-zA-Z0-9À-ſ-\\s]{1,32}"))]
     pub passkey_name: String,
     /// Note: `ToSchema` does currently not exist for `webauthn_rs::prelude::PublicKeyCredential`
     #[schema(value_type = str)]
diff --git a/src/common/src/constants.rs b/src/common/src/constants.rs
index 348d79838..ce1a71e0d 100644
--- a/src/common/src/constants.rs
+++ b/src/common/src/constants.rs
@@ -129,7 +129,7 @@ lazy_static! {
     pub static ref RE_CHALLENGE: Regex = Regex::new(r"^(plain|S256)$").unwrap();
     pub static ref RE_CITY: Regex = Regex::new(r"^[a-zA-Z0-9À-ÿ-]{0,48}$").unwrap();
     pub static ref RE_CLIENT_ID_EPHEMERAL: Regex = Regex::new(r"^[a-zA-Z0-9,.:/_\-&?=~#!$'()*+%]{2,256}$").unwrap();
-    pub static ref RE_CLIENT_NAME: Regex = Regex::new(r"^[a-zA-Z0-9À-ÿ-\s\x{3041}-\x{3096}\x{30A0}-\x{30FF}\x{3400}-\x{4DB5}\x{4E00}-\x{9FCB}\x{F900}-\x{FA6A}\x{2E80}-\x{2FD5}\x{FF66}-\x{FF9F}\x{FFA1}-\x{FFDC}\x{31F0}-\x{31FF}]{2,128}$").unwrap();
+    pub static ref RE_CLIENT_NAME: Regex = Regex::new(r"^[a-zA-Z0-9À-ſ-\s\x{3041}-\x{3096}\x{30A0}-\x{30FF}\x{3400}-\x{4DB5}\x{4E00}-\x{9FCB}\x{F900}-\x{FA6A}\x{2E80}-\x{2FD5}\x{FF66}-\x{FF9F}\x{FFA1}-\x{FFDC}\x{31F0}-\x{31FF}]{2,128}$").unwrap();
     pub static ref RE_CODE_CHALLENGE: Regex = Regex::new(r"^[a-zA-Z0-9-\._~]{43,128}$").unwrap();
     pub static ref RE_CODE_VERIFIER: Regex = Regex::new(r"^[a-zA-Z0-9-\._~+/=]+$").unwrap();
     pub static ref RE_CONTACT: Regex = Regex::new(r"^[a-zA-Z0-9\+.@/:]{0,48}$").unwrap();
@@ -157,7 +157,7 @@ lazy_static! {
     pub static ref RE_SEARCH: Regex = Regex::new(r"^[a-zA-Z0-9,.:/_\-&?=~#!$'()*+%@]+$").unwrap();
     pub static ref RE_STREET: Regex = Regex::new(r"^[a-zA-Z0-9À-ÿ-.\s]{0,48}$").unwrap();
     pub static ref RE_URI: Regex = Regex::new(r"^[a-zA-Z0-9,.:/_\-&?=~#!$'()*+%]+$").unwrap();
-    pub static ref RE_USER_NAME: Regex = Regex::new(r"^[a-zA-Z0-9À-ÿ-\s\x{3041}-\x{3096}\x{30A0}-\x{30FF}\x{3400}-\x{4DB5}\x{4E00}-\x{9FCB}\x{F900}-\x{FA6A}\x{2E80}-\x{2FD5}\x{FF66}-\x{FF9F}\x{FFA1}-\x{FFDC}\x{31F0}-\x{31FF}]{1,32}$").unwrap();
+    pub static ref RE_USER_NAME: Regex = Regex::new(r"^[a-zA-Z0-9À-ſ-\s\x{3041}-\x{3096}\x{30A0}-\x{30FF}\x{3400}-\x{4DB5}\x{4E00}-\x{9FCB}\x{F900}-\x{FA6A}\x{2E80}-\x{2FD5}\x{FF66}-\x{FF9F}\x{FFA1}-\x{FFDC}\x{31F0}-\x{31FF}]{1,32}$").unwrap();
     pub static ref RE_TOKEN_68: Regex = Regex::new(r"^[a-zA-Z0-9-._~+/]+=*$").unwrap();
     pub static ref RE_TOKEN_ENDPOINT_AUTH_METHOD: Regex = Regex::new(r"^(client_secret_post|client_secret_basic|none)$").unwrap();
 


Test patch: