When I use a kratos schema to support email and us...
# ory-network
r
When I use a kratos schema to support email and username as primary identifier. Do I have to do anything in the SDK to signal that? I updated my schema on ory cloud, created a new user (before I migrate existing identities to use the new schema), verified it's using the new schema, but I still can only login with email (which was our only primary identifier before).
Copy code
{
  "$id": "<https://schemas.ory.sh/presets/kratos/identity.email.schema.json>",
  "title": "Person",
  "type": "object",
  "properties": {
    "traits": {
      "type": "object",
      "properties": {
        "email": {
          "type": "string",
          "format": "email",
          "title": "E-Mail",
          "<http://ory.sh/kratos|ory.sh/kratos>": {
            "credentials": {
              "password": {
                "identifier": true
              },
              "totp": {
                "account_name": true
              }
            },
            "recovery": {
              "via": "email"
            },
            "verification": {
              "via": "email"
            }
          },
          "maxLength": 320
        },
        "name": {
          "type": "object",
          "properties": {
            "first": {
              "type": "string",
              "title": "First"
            },
            "last": {
              "type": "string",
              "title": "last"
            }
          }
        },
        "username": {
          "type": "string",
          "title": "username",
          "maxLength": 30,
          "<http://ory.sh/kratos|ory.sh/kratos>": {
            "credentials": {
              "password": {
                "identifier": true
              }
            }
          }
        }
      },
      "required": [
        "email",
        "username"
      ],
      "additionalProperties": false
    }
  }
}
(we started off with a schema which had only email and password (and a few other fields).)
p
Hi @red-machine-69654 You should be able to log in using email or username on the new acount that registered using the new schema. I believe the old users will first need to set a username inside their profile settings.
r
@proud-plumber-24205 I am unable to
all my (old) accounts have a username already... but I think i need to migrate them to the new schema. I added this to the (updated) schema, for `username`:
Copy code
"username": {
// ...
         "<http://ory.sh/kratos|ory.sh/kratos>": {
            "credentials": {
              "password": {
                "identifier": true
              }
            }
          }
weird
ok, i just tried again
😄
it works on the new account
and it works via CLI as well:
Copy code
❯ runway login
runway login
> tillschema           
> ***********          
login user tillschema: done
Logged in as tillschema
Configuration file written to "/Users/till/.cache/runway/runway.json"
yay 😄 — i guess it takes a bit for the schema to be updated/propaged
it does not yet work with "older" accounts, i guess i need to migrate the schema for it to work 😄
@proud-plumber-24205 if you can, what do I do API/sdk wise to update identities? Can I update the schema used by an identity using the patch admin api? https://www.ory.sh/docs/reference/api#operation/adminPatchIdentity
m
Yes @red-machine-69654.
🙏 1
r
@magnificent-energy-493 I am trying to use the sdk to do it... bit of a struggle to inject the PAT i generated on ory-cloud. Any hints? I see that I can set a defaultHeader thing in the server configuration, but is that really it?
Copy code
ID: d9d8d739300700f7947fcd81affd89cf661120d323af3f65de53654499cd61ddcb212f86e56da0332c14b8116b607c4bfcce041a597f6c54ace12c8c710f8057 (created: 0001-01-01 00:00:00 +0000 UTC)
This API is wild.
schema.Id
followed by
schema.GetCreatedAt().String()
@magnificent-energy-493 @high-optician-2097 or anyone ory, can you share an example of how to update a schema on an identity using the SDK? I am stuck here:
Copy code
identity, _, err := a.client.V0alpha2Api.AdminGetIdentity(a.ctx, identityId).Execute()
	if err != nil {
		return err
	}

	identity.SetSchemaId(schemaId)

	updateRequest := a.client.V0alpha2Api.AdminUpdateIdentity(a.ctx, ???)
I was hoping I can do
SetSchemaId
and then pipe it into
AdminUpdateIdentity
but that doesn't seem to work and my google fails me.
p
Hi @red-machine-69654 Have you tried calling the API directly in cURL ? https://www.ory.sh/docs/kratos/reference/api#operation/adminUpdateIdentity
you just need the PAT in the header to access the admin API
r
yeah, i just expected something "more nifty" than setting the DefaultHeader to do this. the pat is working now, just didn't feel right to do it like this?
the question about updating identities (when we made changes to a schema is a bit more pressing)
I think i am using this wrong
Copy code
identity.SetSchemaId(schemaId)

	traits := identity.GetTraits().(map[string]interface{})

	body := ory.NewAdminUpdateIdentityBody(schemaId, *identity.State, traits)

	updateRequest := a.client.V0alpha2Api.AdminUpdateIdentity(a.ctx, identityId)
	updateRequest.AdminUpdateIdentityBody(*body)
	updatedId, http, err := updateRequest.Execute()

	if err != nil {
		logrus.Printf("%v", http)
		logrus.Debug("error running update")
		logrus.Debug(http.Body)
		return err
	}

	if updatedId.SchemaId != schemaId {
		return fmt.Errorf("schema wasn't updated")
	}
this effectively leads to:
Copy code
INFO[0000] &{500 Internal Server Error 500 HTTP/2.0 2 0 map[Alt-Svc:[h3=":443"; ma=86400, h3-29=":443"; ma=86400] B3:[0000000000000000a35fd389bd85f4ec-0dec4873cdfa0878-1] Cache-Control:[private, no-cache, no-store, must-revalidate] Cf-Cache-Status:[DYNAMIC] Cf-Ray:[73f4cedb2e015902-TXL] Content-Length:[170] Content-Type:[application/json] Date:[Tue, 23 Aug 2022 15:16:12 GMT] Expect-Ct:[max-age=604800, report-uri="<https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct>"] Server:[cloudflare] Set-Cookie:[__cflb=0pg1SKa3T3hcJoeWsdKc4AgvxrNdv5LXwoYLHHnT; SameSite=None; Secure; path=/; expires=Tue, 23-Aug-22 16:16:12 GMT; HttpOnly] Uber-Trace-Id:[0000000000000000a35fd389bd85f4ec:0dec4873cdfa0878:0:1] Vary:[Origin] X-B3-Sampled:[1] X-B3-Spanid:[0dec4873cdfa0878] X-B3-Traceid:[0000000000000000a35fd389bd85f4ec]] {{"error":{"code":500,"status":"Internal Server Error","request":"1ed86b49-3647-94bf-b308-2b19c609ca52","message":"invalid character '}' looking for beginning of value"}}
} 170 [] false false map[] 0xc0000c4800 0xc0000f80b0} 
DEBU[0000] error running update                         
DEBU[0000] {{"error":{"code":500,"status":"Internal Server Error","request":"1ed86b49-3647-94bf-b308-2b19c609ca52","message":"invalid character '}' looking for beginning of value"}}
} 
FATA[0000] 500 Internal Server Error                    
exit status 1
p
this seems like a JSON error
r
I could really use some assistance, had not expected to spend all day on the sdk today to perform this
h
It’s pretty difficult to follow these things in slack threads, as code snippets are split across several messages and quickly outdated. Can you maybe provide a small repo that contains what you’re trying to do?
r
i just added
i am trying to update an identity's schema ID
Copy code
// check if schema exists
	_, _, err := a.client.V0alpha2Api.GetIdentitySchema(a.ctx, schemaId).Execute()
	if err != nil {
		logrus.Debug("error getting schema")
		return err
	}

	// check if identity exists
	identity, _, err := a.client.V0alpha2Api.AdminGetIdentity(a.ctx, identityId).Execute()
	if err != nil {
		logrus.Debug("error getting identity")
		return err
	}
	if identity.SchemaId == schemaId {
		fmt.Println("already migrated, no update required")
		return nil
	}

	identity.SetSchemaId(schemaId)

	traits := identity.GetTraits().(map[string]interface{})

	body := ory.NewAdminUpdateIdentityBody(schemaId, *identity.State, traits)

	updateRequest := a.client.V0alpha2Api.AdminUpdateIdentity(a.ctx, identityId)
	updateRequest.AdminUpdateIdentityBody(*body)
	updatedId, http, err := updateRequest.Execute()

	if err != nil {
		logrus.Printf("%v", http)
		logrus.Debug("error running update")
		logrus.Debug(http.Body)
		return err
	}

	if updatedId.SchemaId != schemaId {
		return fmt.Errorf("schema wasn't updated")
	}

	return nil
this is the entire code i am running (sans v0alpha2Api setup, etc.)
h
ok, and the resulting error is the JSON error you posted above?
r
yes
error 500
h
ok, it’s already EoD here so I won’t be able to reproduce the issue today, but I’ll take a look first thing tomorrow 🙂 sorry for the troubles!
r
EoD?
oh, end of day
we are in the same timezone 😃
h
Good morning @red-machine-69654, are you still struggling with this?
r
@high-optician-2097 yeah, haven’t made any changes
h
kk
omg my ide just doesn’t start ._.
r
Emacs? 🧌
h
yeah i should take this as a sign to use vim
r
You find anything, or happen to have some code I can look?
h
I just got my IDE working, please give me some time. I have to deprioritize a lot of other things to help here so I’m already doing the best I can
@red-machine-69654 the identity schema ID, are you using a custom identity schema or are you using a preset?
r
Custom, I pulled it with listSchemas or something
h
ty
Ok, I can reproduce the issue - thank you 🙂 Investigating now what is going on there
Hm, so I was able to reproduce the issue when using a JSON schema that kratos does not know. But if set up correctly, it works for me:
Copy code
package main

import (
	"context"
	"fmt"
	"<http://github.com/sirupsen/logrus|github.com/sirupsen/logrus>"
)
import "<http://github.com/ory/client-go|github.com/ory/client-go>"

var YOUR_ORY_PAT="...."
var YOUR_ORY_SDK_URL="<https://YOUR_PROJECT.projects.oryapis.com>"

func main() {
	if err := migrateSchema("<preset://email>", "f086edf7eb1d4e78731d4409303cdd173fab4b1e6ca79c09871290103d7c22072c252773792b29621f9ac272ced5ffa9ee63420a81f6dff6850c07a7cb26c2e4", "3974b38c-6622-417a-a1c2-532b06f67264"); err != nil {
		panic(err)
	}
}

func migrateSchema(fromSchema, toSchema, identityID string) error {
	conf := client.NewConfiguration()
	conf.Servers = client.ServerConfigurations{
		{
			URL: YOUR_ORY_SDK_URL,
		},
	}
	oc := client.NewAPIClient(conf)

	ctx := context.Background()
	ctx = context.WithValue(ctx, client.ContextAccessToken, YOUR_PAT)

	// check if schema exists
	_, _, err := oc.V0alpha2Api.GetIdentitySchema(ctx, toSchema).Execute()
	if err != nil {
		logrus.Debug("error getting schema")
		return err
	}

	// check if identity exists
	identity, _, err := oc.V0alpha2Api.AdminGetIdentity(ctx, identityID).Execute()
	if err != nil {
		logrus.Debug("error getting identity")
		return err
	}
	if identity.SchemaId == toSchema {
		fmt.Println("already migrated, no update required")
		return nil
	}

	traits := identity.GetTraits().(map[string]interface{})
	body := client.NewAdminUpdateIdentityBody(toSchema, *identity.State, traits)

	updatedId, _, err := oc.V0alpha2Api.AdminUpdateIdentity(ctx, identityID).AdminUpdateIdentityBody(*body).Execute()
	if err != nil {
		logrus.Debug("error running update")
		return err
	}

	if updatedId.SchemaId != toSchema {
		return fmt.Errorf("schema wasn't updated")
	}

	fmt.Println("done")

	return nil
}
Copy code
fromSchema
is actually not needed
r
Weird, I am actually listening/getting the schema beforehand to make sure I didn’t typo the id or something
h
ahh
i know what the problem is
you had:
Copy code
updateRequest := oc.V0alpha2Api.AdminUpdateIdentity(ctx, identityId)
	updateRequest.AdminUpdateIdentityBody(*body)
	updatedId, http, err := updateRequest.Execute()
but updateRequest.AdminUpdateIdentityBody returns a value
and you do not update it, hence the body is empty
Copy code
updateRequest := oc.V0alpha2Api.AdminUpdateIdentity(ctx, identityId)
	updateRequest = updateRequest.AdminUpdateIdentityBody(*body)
	updatedId, http, err := updateRequest.Execute()
this probably works also
r
Okay, I‘ll try that when I am back
h
the code i posted above worked fine for me - of course you’ll need to update your values etc
👍 1
r
@high-optician-2097 code works, but it doesn't seem to recognize the username field still
traits look identical structure wise
is there another step to do migration wise? or do i have to re-save the identity
something is off
ok
my colleague somehow managed to register twice
with the same email, but different schemas
is that expected? 😃
h
depends on your schema