Skip to content
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

Internal Server Error on Identity Credential Request for Identities registered with Native OIDC Flow #3888

Open
4 of 5 tasks
aptgetgit opened this issue Apr 22, 2024 · 0 comments
Labels
bug Something is not working.

Comments

@aptgetgit
Copy link

aptgetgit commented Apr 22, 2024

Preflight checklist

Ory Network Project

No response

Describe the bug

My setup :
Cloudflare -> Nginx -> Oathkeeper(decision api) -> Kratos.
Kratos is configured for password less code login and Google OIDC login, Self Service and registration is enabled.
Bug :
If registering a Google account via native flow using Flutter/Dart ORY client and then retrieving OIDC credential details via the admin API endpoint admin/identities/<id>?include_credential=oidc throws an error json{"error":{"code":500,"status":"Internal Server Error","reason":"Unable to decode hex encrypted string","message":"An internal server error occurred, please contact the system administrator"}} . Retrieving identity without including credential query admin/identities/<id> works perfectly.
It only happens when the account is registered using Native OIDC Flow, as using browser flow via Self-Service UI works perfectly both with query and no query.

Also registration seems to be working in the native flow cause session token is received after the registration flow.

Reproducing the bug

Version
Flutter 3.19.6
Dart SDK version: 3.3.4

Flutter Code

final res = await dio
          .get('https://api.example.com/auth/self-service/registration/api');
      final data = Map<String, dynamic>.from(res.data);
      final flowId = data['id'];
      if (credentialManager.isSupportedPlatform) {
        await credentialManager.init(
          preferImmediatelyAvailableCredentials: true,
          googleClientId: clientId,
        );
        final gCredential = await credentialManager.saveGoogleCredential(
          nonce: Nonce(nonce: "jedn23iudbuyfb"),
        );
        final idToken = gCredential!.idToken;

        var body = UpdateRegistrationFlowWithOidcMethod((b) => b
          ..idToken = idToken
          ..idTokenNonce = 'jedn23iudbuyfb'
          ..method = 'oidc'
          ..provider = 'google');

        final response = await ory.getFrontendApi().updateRegistrationFlow(
          flow: flowId,
          updateRegistrationFlowBody: UpdateRegistrationFlowBody(
              (b) => b..oneOf = OneOf.fromValue1(value: body)),
        );
        final oryData = response.data;
      }
      ```

### Relevant log output

```shell
time=2024-04-21T23:26:49Z level=info msg=started handling request http_request=map[headers:map[accept:*/* accept-encoding:gzip, br cdn-loop:cloudflare cf-connecting-ip:2401:0000:0000:0000:0000:0000:9a21:d8ae cf-ipcountry:NN cf-ray:000020003ef500a6-CDG cf-visitor:{"scheme":"https"} connection:close content-type:application/json user-agent:curl/7.81.0 x-forwarded-for:172.71.123.27 x-forwarded-proto:https x-forwarded-scheme:https x-real-ip:172.71.123.27] host:kratos.admin-domain.com method:GET path:/admin/identities/1c9fef99-8414-4395-a917-041e1d6e9e57 query:REDACTED remote:172.21.0.2:49112 scheme:http]


time=2024-04-21T23:26:49Z level=error msg=An error occurred while handling a request audience=application error=map[debug: message:An internal server error occurred, please contact the system administrator reason:Unable to decode hex encrypted string status:Internal Server Error status_code:500] http_request=map[headers:map[accept:*/* accept-encoding:gzip, br cdn-loop:cloudflare cf-connecting-ip:2401:0000:1c29:0000:0000:0000:9a21:d8ae cf-ipcountry:NN cf-ray:80000000a6-CDG cf-visitor:{"scheme":"https"} connection:close content-type:application/json user-agent:curl/7.81.0 x-forwarded-for:172.71.123.27 x-forwarded-proto:https x-forwarded-scheme:https x-real-ip:172.71.123.27] host:kratos.admin-domain.com method:GET path:/admin/identities/1c9fef99-8414-4395-a917-041e1d6e9e57 query:REDACTED remote:172.21.0.2:49112 scheme:http] http_response=map[status_code:500] service_name=Ory Kratos service_version=v1.1.0


time=2024-04-21T23:26:49Z level=info msg=completed handling request http_request=map[headers:map[accept:*/* accept-encoding:gzip, br cdn-loop:cloudflare cf-connecting-ip:2401:0000:0000:0000:0000:0000:9a21:d8ae cf-ipcountry:NN cf-ray:800000000-CDG cf-visitor:{"scheme":"https"} connection:close content-type:application/json user-agent:curl/7.81.0 x-forwarded-for:172.71.123.27 x-forwarded-proto:https x-forwarded-scheme:https x-real-ip:172.71.123.27] host:kratos.admin-domain.com method:GET path:/admin/identities/1c9fef99-8414-4395-a917-041e1d6e9e57 query:REDACTED remote:172.21.0.2:49112 scheme:http] http_response=map[headers:map[cache-control:private, no-cache, no-store, must-revalidate content-type:application/json] size:192 status:500 text_status:Internal Server Error took:4.327744ms]

Relevant configuration

**KRATOS CONFIG**

version: v1.1.0

dsn: memory # Replaced by PostgreSQL 14 via ENV

identity:
  default_schema_id: user-v1
  schemas:
    - id: user-v1
      url: file:///etc/config/kratos/user.schema.json


selfservice:
  default_browser_return_url: https://accounts.example.com
  allowed_return_urls:
    - https://accounts.example.com

  flows:
    logout:
      after:
        default_browser_return_url: https://accounts.example.com/login

    registration:
      enabled: true
      login_hints: false
      ui_url: https://accounts.example.com/registration
      lifespan: 1h
      after:
        code:
          hooks:
            - hook: session
          default_browser_return_url: https://accounts.example.com
          
        oidc:
          hooks:
            - hook: session
          default_browser_return_url: https://accounts.example.com
        default_browser_return_url: https://accounts.example.com

    login:
      ui_url: https://accounts.example.com/login
      lifespan: 1h
      after:
        oidc:
          hooks:
            - hook: require_verified_address
          default_browser_return_url: https://accounts.example.com
        code:
          hooks:
            - hook: require_verified_address
          default_browser_return_url: https://accounts.example.com
        default_browser_return_url: https://accounts.example.com

    verification:
      enabled: true
      ui_url: https://accounts.example.com/verification
      lifespan: 1h
      use: code
      notify_unknown_recipients: false
      after:
        default_browser_return_url: https://accounts.example.com
    
    recovery:
      enabled: true
      ui_url: https://accounts.example.com/recovery
      lifespan: 1h
      use: code
      notify_unknown_recipients: false
      after:
        default_browser_return_url: https://accounts.example.com

    error:
      ui_url: https://accounts.example.com/errors
    
    settings:
      ui_url: https://accounts.example.com/settings
      lifespan: 1h
      privileged_session_max_age: 15m
      required_aal: highest_available
      after:
        oidc:
          hooks:
            - hook: revoke_active_sessions
          default_browser_return_url: https://accounts.example.com/login

  methods:
    password:
      enabled: false

    code:
      passwordless_enabled: true
      mfa_enabled: false
      passwordless_login_fallback_enabled: true
      enabled: true
      config:
        lifespan: 10m

    oidc:
      enabled: true
      config:
        providers:
          - id: google
            provider: google
            client_id: "ENV"
            client_secret: "ENV"
            issuer_url: https://accounts.google.com
            auth_url: https://accounts.google.com/o/oauth2/v2/auth
            token_url: https://oauth2.googleapis.com/token
            mapper_url:  file:///etc/config/kratos/oidc.google.jsonnet
            scope:
              - email
            subject_source: userinfo
            claims_source: id_token
            requested_claims:
              id_token:
                email:
                  essential: true
                email_verified:
                  essential: true
        base_redirect_uri: https://api.example.com/auth
    
    profile:
      enabled: false

courier:
  message_retries: 2
  delivery_strategy: smtp
  smtp:
    from_address: no-reply@example.com
    from_name: Example
    connection_uri: smtp://thetoken:thetoken@smtp.postmarkapp.com:587/

preview:
  default_read_consistency_level: strong

serve:
  public:
    host: 0.0.0.0
    port: 4433
    base_url: https://api.example.com/auth
    request_log:
      disable_for_health: true
    cors:
      enabled: true
      allow_credentials: true
      options_passthrough: false
      max_age: 0
      debug: false
      allowed_methods:
        - POST
        - GET
        - PUT
        - PATCH
        - DELETE
      allowed_origins:
        - https://example.com
        - https://*.example.com
      allowed_headers:
        - Authorization
        - Cookie
        - Content-Type
      exposed_headers:
        - Content-Type
        - Set-Cookie
  admin:
    host: 0.0.0.0
    port: 4434
    base_url: https://kratos.admin-domain.com
    request_log:
      disable_for_health: true

log:
  level: info
  format: text
  leak_sensitive_values: false
  redaction_text: "REDACTED"

secrets:
  cookie:
    - ipsumipsumipsumi
  cipher:
    - ipsumipsumipsumipsumipsumipsumip
  default:
    - ipsumipsumipsumi

ciphers:
  algorithm: xchacha20-poly1305

cookies:
  same_site: Lax
  domain: example.com
  path: /

session:
  lifespan: 720h
  cookie:
    name: example_com_session
    persistent: true
    same_site: Lax
    domain: example.com
    path: /
  earliest_possible_extend: 120h
  whoami:
    required_aal: highest_available

**OIDC JSONNET**

local claims = {
  email_verified: false,
} + std.extVar('claims');

{
  identity: {
    traits: {
      [if 'email' in claims && claims.email_verified then 'email' else null]: claims.email,
    },
    verified_addresses: std.prune([
      if 'email' in claims && claims.email_verified then { via: 'email', value: claims.email },
    ]),
  },
}


**OATHKEEPER CONFIG**
FYI: Admin API is not authenticated in nginx so no rules in oathkeeper

version: v0.40.7

serve:
  proxy:
    port: 4455
    host: 0.0.0.0
    cors:
      enabled: true
      allow_credentials: true
      max_age: 0
      allowed_methods:
        - POST
        - GET
        - PUT
        - PATCH
        - DELETE
      allowed_origins:
        - https://example.com
        - https://*.example.com
      allowed_headers:
        - Authorization
        - Content-Type
      exposed_headers:
        - Content-Type
      debug: true
  api:
    port: 4456
    host: 0.0.0.0

errors:
  fallback:
    - json

  handlers:
    redirect:
      enabled: true
      config:
        to: https://accounts.example.com/login
        code: 302
        return_to_query_param: "return_to"
        when:
          - error:
              - unauthorized
              - forbidden
            request:
              header:
                accept:
                  - text/html
    json:
      enabled: true
      config:
        verbose: true

access_rules:
  matching_strategy: glob
  repositories:
    - file:///etc/config/oathkeeper/access-rules.yml

authenticators:
  anonymous:
    enabled: true
    config:
      subject: guest
  cookie_session:
    enabled: true
    config:
      check_session_url: http://kratos:4433/sessions/whoami
      preserve_query: true
      preserve_host: true
      preserve_path: true
      subject_from: "identity.id"
      only:
        - example_com_session
  noop:
    enabled: true

authorizers:
  allow:
    enabled: true

mutators:
  noop:
    enabled: true
  header:
    enabled: true
    config:
      headers:
        X-User: "{{ print .Subject }}"

log:
  level: info
  format: text
  leak_sensitive_values: false
  redaction_text: "REDACTED"

Access Rules -
- id: "ory:kratos:public"
  match:
    url: "https://api.example.com/auth/<**>"
    methods:
      - GET
      - POST
      - PUT
      - DELETE
      - PATCH
  authenticators:
    - handler: noop
  authorizer:
    handler: allow
  mutators:
    - handler: noop

- id: "ory:kratos-selfservice-ui-node:anonymous"
  match:
    url: "https://accounts.example.com/<{,self-service,registration,welcome,recovery,verification,login,error,health/{alive,ready},**.css,**.js,**.png,**.svg,**.woff*}>"
    methods:
      - GET
  authenticators:
    - handler: anonymous
  authorizer:
    handler: allow
  mutators:
    - handler: noop

- id: "ory:kratos-selfservice-ui-node:protected"
  match:
    url: "https://accounts.example.com/<{sessions,settings}>"
    methods:
      - GET
  authenticators:
    - handler: cookie_session
  authorizer:
    handler: allow
  mutators:
    - handler: noop
  errors:
    - handler: redirect
      config:
        to: https://accounts.example.com/login

**NGINX CONFIG**

ADMIN API (kratos.admin-domain.com.conf)-
location /admin/ {
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Scheme $scheme;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_pass http://kratos-admin;
  }

  location /identities/ {
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Scheme $scheme;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_pass http://kratos-admin;
  }

Kratos Api (api.example.com.conf) -
location /auth/ {
    auth_request /auth-decision;
    auth_request_set $next_uri $upstream_http_X_Original_URI;
    
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Scheme $scheme;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_pass http://kratos-public/;
  }

Version

1.1.0

On which operating system are you observing this issue?

Other

In which environment are you deploying?

Docker Compose

Additional Context

All of the browser flow has been tested with all the configurations mentioned above, i am guessing either my native code is wrong or there might be a bug with kratos.

@aptgetgit aptgetgit added the bug Something is not working. label Apr 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something is not working.
Projects
None yet
Development

No branches or pull requests

1 participant