UAA get access token and refresh token

1,281 views
Skip to first unread message

david.webe...@gmail.com

unread,
May 5, 2014, 8:39:58 AM5/5/14
to vcap...@cloudfoundry.org
Dear experts,

If I'm getting the authorize code over the path /oauth/authorize with the following params:
  • authorization: Basic "loginuser:loginuserpassword".base64
  • username={"username":"userA"}
  • response_type=code
  • source=login
  • client_id=cf
  • grant_type=password
  • redirect_uri={{cf-uaa}}/oauth/token
after that I like to get the access token and the refresh token for "userA".
I did the following request /oauth/token with params like:
  • authorization: Basic "loginuser:loginuserpassword".base64
  • grant_type=authorization_code
  • code=my-code-from-above
I always getting "error": "redirect_uri_mismatch", "error_description": "Redirect URI mismatch.".

Do I need a redirect uri for call /oauth/token and why?

Shannon Coen

unread,
May 12, 2014, 10:20:36 PM5/12/14
to vcap...@cloudfoundry.org, david.webe...@gmail.com, Ryan Morgan
Your example implies that your redirect_uri is the url of UAA. 

redirect_uri must be configured when the client is created in uaa-db.

redirect_uri must also be passed with call to /oauth/authorize. This declares the callback URL that the UAA should redirect to after the code is issued. The domain must match that of the redirect_uri configured for the client in uaa-db.

I wasn't aware that redirect_uri must also be provided with call to /oauth/token, but this could serve the same purpose as for the call to /oauth/authorize; to declare the callback URL.


cc'ing Ryan Morgan, PM for UAA.

Ryan Morgan

unread,
May 13, 2014, 2:00:59 PM5/13/14
to vcap...@cloudfoundry.org, david.webe...@gmail.com, Ryan Morgan

Hi David,

You shouldn't need to pass the grant_type to the call to /oauth/token, you should only need to pass the code as a parameter in the request body. (see: https://github.com/cloudfoundry/uaa/blob/master/docs/UAA-APIs.rst#oauth2-token-endpoint-post-oauthtoken)

To address issues like this in our docs we've been attempting to add 'curl' examples to our docs, I'll see if we can get that added here as well to make this more clear.

-Ryan



To unsubscribe from this group and stop receiving emails from it, send an email to vcap-dev+u...@cloudfoundry.org.

Philip Kuryloski

unread,
May 13, 2014, 2:35:28 PM5/13/14
to Ryan Morgan, vcap...@cloudfoundry.org, david.webe...@gmail.com, Ryan Morgan
We've added some of the examples with curl https://github.com/cloudfoundry/uaa/blob/develop/docs/UAA-APIs.rst#authorization-code-grant
These doc changes will move to the master branch at the next release.  As Ryan mentioned, the redirect_uri is provided to the /oauth/authorize endpoint, rather than the /oauth/token endpoint.  Let us know if the docs are still unclear, and we'll try to clarify them.

-Phil

david.webe...@gmail.com

unread,
May 13, 2014, 4:34:33 PM5/13/14
to vcap...@cloudfoundry.org, david.webe...@gmail.com, Ryan Morgan
Thanks for you answer Ryan.
As of now I don't do following the redirects(Status code 302) anymore, so it isn't important to me to have the right redirect as I can get the desired parameter from the header fragment.

david.webe...@gmail.com

unread,
May 13, 2014, 5:02:46 PM5/13/14
to vcap...@cloudfoundry.org, david.webe...@gmail.com, Ryan Morgan
I think it's the best to share my workflow as you can see what I'm doing and what I expect.

Current implementation situation:
1. The users enters the api endpoint, username and password in cf CLI.

2. A request to the representativ login server (in our case a Drupal site) will get above call:
     -H "Content-Type: application/x-www-form-urlencoded" \
     -H "Accept: application/json" \
     -H "Cache-Control: no-cache" \
     -H "Authorization: Basic Y2Y6" \
     -d "grant_type=password" \
     -d "username=my-user" \
     -d "password=my-password" \
     -m 30 \
     -v \

3. The Drupal site will make a call to get the access token for the login user, which has the privilege to get tokens for the cf user.
This token will be used in the next call and we name the token login-user-token
     -H "Cache-Control: no-cache" \
     -H "Accept: application/json" \
     -H "Authorization: Basic base64(login:login-user-password)" \
     -H "Content-Type: application/x-www-form-urlencoded" \
     -d "response_type=token" \
     -d "grant_type=client_credentials" \
     -m 30 \
     -v \

4. We now call the authorize route to acquire a token for the initial user in request 2.
     -H "Cache-Control: no-cache" \
     -H "Authorization: bearer login-user-token" \
     -H "Accept: application/json" \
     -d "username={"username":"my-user"}" \
     -d "response_type=token" \
     -d "source=login" \
     -d "client_id=cf" \
     -d "grant_type=password" \
     -d "redirect_uri=http%3A%2F%2Flocalhost" \
     -m 30 \
     -v \

5. If I don't follow the redirect (Status code 302) for call 4. I can extract the token for the user from the header fragment and send this back to the cf CLI and the user can work until the token expires ;)

So this case worked for us quite a long time but now with the next version of the cf CLI (6.1.1) you will have a refresh_token request before every cf push command (not only cf push by the way).
So the user has to refresh his access token. The problem is that I don't have the refresh token as it's not provided by the response/fragment of request in 4.

So how can I acquire an access- and refresh token?

Solutions I tried:
A. I tried to directly call the 3. reuest but with the user credentials but I get a 401 (Unauthorised)
B. I changed the call 4. to provide me with a "code" instead of a "token" and then call some sort of call 3. again to exchange my code with a access token for the user.

Thanks for your help as I like to implement this the right way and don't like to do some hacking stuff.

Cheers
Dave


Filip Hanik

unread,
May 13, 2014, 9:17:19 PM5/13/14
to vcap...@cloudfoundry.org, david.webe...@gmail.com, Ryan Morgan
describe your setup a bit, are all your users managed by the UAA, and stored in the UAA database? if so....

...you could skip step 1-3 and do only step 4
in step 4, you would access /oauth/token instead of /oauth/authorize, and this would return you an access token + refresh token

What you need is
1. provide a password parameter for the user
2. remove the redirect_uri parameter, you don't need it for a grant_type=password call
3. remove the source=login parameter 

it will look like:

curl -X POST "https://the-cf-uaa/oauth/token" \
     -H "Cache-Control: no-cache" \
     -u "cf:" \
     -H "Accept: application/json" \
     -d "username=my-user" \
     -d "password=my-password" \
     -d "response_type=token" \
     -d "client_id=cf" \
     -d "grant_type=password" \
     -m 30 \
     -v

and voila, you have access and refresh token. Against a stand alone UAA from github with in memory DB, it would look like (user/password = marissa/koala)

     -H "Cache-Control: no-cache" \
     -u "cf:" \
     -H "Accept: application/json" \
     -d "username=marissa" \
     -d "password=koala" \
     -d "response_type=token" \
     -d "client_id=cf" \
     -d "grant_type=password" \
     -m 30 \
     -v




Message has been deleted

david.webe...@gmail.com

unread,
May 14, 2014, 8:49:15 AM5/14/14
to vcap...@cloudfoundry.org, david.webe...@gmail.com, Ryan Morgan
That was also my first intention but sadly if I do this request:
     -H "Accept: application/json" \
     -H "Cache-Control: no-cache" \
     -H "Content-Type: application/x-www-form-urlencoded" \
     -H "Authorization: Basic Y2Y6" \
     -d "grant_type=password" \
     -d "username=my-user" \
     -d "password=my-password" \
     -d "response_type=token" \
     -d "client_id=cf" \
     -m 30 \
     -v \

I get this response with 401:

{"error": "unauthorized",  "error_description": "Bad credentials"}

david.webe...@gmail.com

unread,
May 15, 2014, 5:32:23 AM5/15/14
to vcap...@cloudfoundry.org, david.webe...@gmail.com, Ryan Morgan
I think I have the problem located for the request in the upper post.
We have the password not set in the uaa because we are using our own login server.

So I tried to set the password over the CLI for my user and the above exception was raised.
1. Is it a problem if the password is not set?
2. How can I set the password without CLI or RestClient?

I really appreciate your help and thanks
Dave

{

  "exception": {

    "cause": null,

    "stackTrace": [

      {

        "methodName": "handleException",

        "fileName": "PasswordChangeEndpoint.java",

        "lineNumber": 93,

        "className": "org.cloudfoundry.identity.uaa.password.PasswordChangeEndpoint",

        "nativeMethod": false

      },

      {

        "methodName": "invoke",

        "fileName": "<generated>",

        "lineNumber": -1,

        "className": "org.cloudfoundry.identity.uaa.password.PasswordChangeEndpoint$$FastClassByCGLIB$$9ce9299a",

        "nativeMethod": false

      },

      {

        "methodName": "invoke",

        "fileName": "MethodProxy.java",

        "lineNumber": 204,

        "className": "net.sf.cglib.proxy.MethodProxy",

        "nativeMethod": false

      },

      {

        "methodName": "invokeJoinpoint",

        "fileName": "Cglib2AopProxy.java",

        "lineNumber": 689,

        "className": "org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation",

        "nativeMethod": false

      },

      {

        "methodName": "proceed",

        "fileName": "ReflectiveMethodInvocation.java",

        "lineNumber": 150,

        "className": "org.springframework.aop.framework.ReflectiveMethodInvocation",

        "nativeMethod": false

      },

      {

        "methodName": "invoke",

        "fileName": "ExposeInvocationInterceptor.java",

        "lineNumber": 90,

        "className": "org.springframework.aop.interceptor.ExposeInvocationInterceptor",

        "nativeMethod": false

      },

      {

        "methodName": "proceed",

        "fileName": "ReflectiveMethodInvocation.java",

        "lineNumber": 172,

        "className": "org.springframework.aop.framework.ReflectiveMethodInvocation",

        "nativeMethod": false

      },

      {

        "methodName": "intercept",

        "fileName": "Cglib2AopProxy.java",

        "lineNumber": 622,

        "className": "org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor",

        "nativeMethod": false

      },

      {

        "methodName": "handleException",

        "fileName": "<generated>",

        "lineNumber": -1,

        "className": "org.cloudfoundry.identity.uaa.password.PasswordChangeEndpoint$$EnhancerByCGLIB$$3cbcc67f",

        "nativeMethod": false

      },

      {

        "methodName": "invoke0",

        "fileName": "NativeMethodAccessorImpl.java",

        "lineNumber": -2,

        "className": "sun.reflect.NativeMethodAccessorImpl",

        "nativeMethod": true

      },

      {

        "methodName": "invoke",

        "fileName": "NativeMethodAccessorImpl.java",

        "lineNumber": 57,

        "className": "sun.reflect.NativeMethodAccessorImpl",

        "nativeMethod": false

      },

      {

        "methodName": "invoke",

        "fileName": "DelegatingMethodAccessorImpl.java",

        "lineNumber": 43,

        "className": "sun.reflect.DelegatingMethodAccessorImpl",

        "nativeMethod": false

      },

      {

        "methodName": "invoke",

        "fileName": "Method.java",

        "lineNumber": 616,

        "className": "java.lang.reflect.Method",

        "nativeMethod": false

      },

      {

        "methodName": "invoke",

        "fileName": "InvocableHandlerMethod.java",

        "lineNumber": 213,

        "className": "org.springframework.web.method.support.InvocableHandlerMethod",

        "nativeMethod": false

      },

      {

        "methodName": "invokeForRequest",

        "fileName": "InvocableHandlerMethod.java",

        "lineNumber": 126,

        "className": "org.springframework.web.method.support.InvocableHandlerMethod",

        "nativeMethod": false

      },

      {

        "methodName": "invokeAndHandle",

        "fileName": "ServletInvocableHandlerMethod.java",

        "lineNumber": 96,

        "className": "org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod",

        "nativeMethod": false

      },

      {

        "methodName": "doResolveHandlerMethodException",

        "fileName": "ExceptionHandlerExceptionResolver.java",

        "lineNumber": 275,

        "className": "org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver",

        "nativeMethod": false

      },

      {

        "methodName": "doResolveException",

        "fileName": "AbstractHandlerMethodExceptionResolver.java",

        "lineNumber": 60,

        "className": "org.springframework.web.servlet.handler.AbstractHandlerMethodExceptionResolver",

        "nativeMethod": false

      },

      {

        "methodName": "resolveException",

        "fileName": "AbstractHandlerExceptionResolver.java",

        "lineNumber": 136,

        "className": "org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver",

        "nativeMethod": false

      },

      {

        "methodName": "processHandlerException",

        "fileName": "DispatcherServlet.java",

        "lineNumber": 1120,

        "className": "org.springframework.web.servlet.DispatcherServlet",

        "nativeMethod": false

      },

      {

        "methodName": "doDispatch",

        "fileName": "DispatcherServlet.java",

        "lineNumber": 944,

        "className": "org.springframework.web.servlet.DispatcherServlet",

        "nativeMethod": false

      },

      {

        "methodName": "doService",

        "fileName": "DispatcherServlet.java",

        "lineNumber": 852,

        "className": "org.springframework.web.servlet.DispatcherServlet",

        "nativeMethod": false

      },

      {

        "methodName": "processRequest",

        "fileName": "FrameworkServlet.java",

        "lineNumber": 882,

        "className": "org.springframework.web.servlet.FrameworkServlet",

        "nativeMethod": false

      },

      {

        "methodName": "doPut",

        "fileName": "FrameworkServlet.java",

        "lineNumber": 800,

        "className": "org.springframework.web.servlet.FrameworkServlet",

        "nativeMethod": false

      },

      {

        "methodName": "service",

        "fileName": "HttpServlet.java",

        "lineNumber": 650,

        "className": "javax.servlet.http.HttpServlet",

        "nativeMethod": false

      },

      {

        "methodName": "service",

        "fileName": "HttpServlet.java",

        "lineNumber": 728,

        "className": "javax.servlet.http.HttpServlet",

        "nativeMethod": false

      },

      {

        "methodName": "internalDoFilter",

        "fileName": "ApplicationFilterChain.java",

        "lineNumber": 305,

        "className": "org.apache.catalina.core.ApplicationFilterChain",

        "nativeMethod": false

      },

      {

        "methodName": "doFilter",

        "fileName": "ApplicationFilterChain.java",

        "lineNumber": 210,

        "className": "org.apache.catalina.core.ApplicationFilterChain",

        "nativeMethod": false

      },

      {

        "methodName": "doFilter",

        "fileName": "FilterChainProxy.java",

        "lineNumber": 330,

        "className": "org.springframework.security.web.FilterChainProxy$VirtualFilterChain",

        "nativeMethod": false

      },

      {

        "methodName": "invoke",

        "fileName": "FilterSecurityInterceptor.java",

        "lineNumber": 118,

        "className": "org.springframework.security.web.access.intercept.FilterSecurityInterceptor",

        "nativeMethod": false

      },

      {

        "methodName": "doFilter",

        "fileName": "FilterSecurityInterceptor.java",

        "lineNumber": 84,

        "className": "org.springframework.security.web.access.intercept.FilterSecurityInterceptor",

        "nativeMethod": false

      },

      {

        "methodName": "doFilter",

        "fileName": "FilterChainProxy.java",

        "lineNumber": 342,

        "className": "org.springframework.security.web.FilterChainProxy$VirtualFilterChain",

        "nativeMethod": false

      },

      {

        "methodName": "doFilter",

        "fileName": "ExceptionTranslationFilter.java",

        "lineNumber": 113,

        "className": "org.springframework.security.web.access.ExceptionTranslationFilter",

        "nativeMethod": false

      },

      {

        "methodName": "doFilter",

        "fileName": "FilterChainProxy.java",

        "lineNumber": 342,

        "className": "org.springframework.security.web.FilterChainProxy$VirtualFilterChain",

        "nativeMethod": false

      },

      {

        "methodName": "doFilter",

        "fileName": "AnonymousAuthenticationFilter.java",

        "lineNumber": 113,

        "className": "org.springframework.security.web.authentication.AnonymousAuthenticationFilter",

        "nativeMethod": false

      },

      {

        "methodName": "doFilter",

        "fileName": "FilterChainProxy.java",

        "lineNumber": 342,

        "className": "org.springframework.security.web.FilterChainProxy$VirtualFilterChain",

        "nativeMethod": false

      },

      {

        "methodName": "doFilter",

        "fileName": "SecurityContextHolderAwareRequestFilter.java",

        "lineNumber": 54,

        "className": "org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter",

        "nativeMethod": false

      },

      {

        "methodName": "doFilter",

        "fileName": "FilterChainProxy.java",

        "lineNumber": 342,

        "className": "org.springframework.security.web.FilterChainProxy$VirtualFilterChain",

        "nativeMethod": false

      },

      {

        "methodName": "doFilter",

        "fileName": "OAuth2AuthenticationProcessingFilter.java",

        "lineNumber": 131,

        "className": "org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter",

        "nativeMethod": false

      },

      {

        "methodName": "doFilter",

        "fileName": "FilterChainProxy.java",

        "lineNumber": 342,

        "className": "org.springframework.security.web.FilterChainProxy$VirtualFilterChain",

        "nativeMethod": false

      },

      {

        "methodName": "doFilter",

        "fileName": "SecurityContextPersistenceFilter.java",

        "lineNumber": 87,

        "className": "org.springframework.security.web.context.SecurityContextPersistenceFilter",

        "nativeMethod": false

      },

      {

        "methodName": "doFilter",

        "fileName": "FilterChainProxy.java",

        "lineNumber": 342,

        "className": "org.springframework.security.web.FilterChainProxy$VirtualFilterChain",

        "nativeMethod": false

      },

      {

        "methodName": "doFilter",

        "fileName": "SecurityFilterChainPostProcessor.java",

        "lineNumber": 186,

        "className": "org.cloudfoundry.identity.uaa.security.web.SecurityFilterChainPostProcessor$UaaLoggingFilter",

        "nativeMethod": false

      },

      {

        "methodName": "doFilter",

        "fileName": "FilterChainProxy.java",

        "lineNumber": 342,

        "className": "org.springframework.security.web.FilterChainProxy$VirtualFilterChain",

        "nativeMethod": false

      },

      {

        "methodName": "doFilterInternal",

        "fileName": "FilterChainProxy.java",

        "lineNumber": 192,

        "className": "org.springframework.security.web.FilterChainProxy",

        "nativeMethod": false

      },

      {

        "methodName": "doFilter",

        "fileName": "FilterChainProxy.java",

        "lineNumber": 160,

        "className": "org.springframework.security.web.FilterChainProxy",

        "nativeMethod": false

      },

      {

        "methodName": "invokeDelegate",

        "fileName": "DelegatingFilterProxy.java",

        "lineNumber": 346,

        "className": "org.springframework.web.filter.DelegatingFilterProxy",

        "nativeMethod": false

      },

      {

        "methodName": "doFilter",

        "fileName": "DelegatingFilterProxy.java",

        "lineNumber": 259,

        "className": "org.springframework.web.filter.DelegatingFilterProxy",

        "nativeMethod": false

      },

      {

        "methodName": "internalDoFilter",

        "fileName": "ApplicationFilterChain.java",

        "lineNumber": 243,

        "className": "org.apache.catalina.core.ApplicationFilterChain",

        "nativeMethod": false

      },

      {

        "methodName": "doFilter",

        "fileName": "ApplicationFilterChain.java",

        "lineNumber": 210,

        "className": "org.apache.catalina.core.ApplicationFilterChain",

        "nativeMethod": false

      },

      {

        "methodName": "invoke",

        "fileName": "StandardWrapperValve.java",

        "lineNumber": 222,

        "className": "org.apache.catalina.core.StandardWrapperValve",

        "nativeMethod": false

      },

      {

        "methodName": "invoke",

        "fileName": "StandardContextValve.java",

        "lineNumber": 123,

        "className": "org.apache.catalina.core.StandardContextValve",

        "nativeMethod": false

      },

      {

        "methodName": "invoke",

        "fileName": "AuthenticatorBase.java",

        "lineNumber": 502,

        "className": "org.apache.catalina.authenticator.AuthenticatorBase",

        "nativeMethod": false

      },

      {

        "methodName": "invoke",

        "fileName": "StandardHostValve.java",

        "lineNumber": 171,

        "className": "org.apache.catalina.core.StandardHostValve",

        "nativeMethod": false

      },

      {

        "methodName": "invoke",

        "fileName": "ErrorReportValve.java",

        "lineNumber": 99,

        "className": "org.apache.catalina.valves.ErrorReportValve",

        "nativeMethod": false

      },

      {

        "methodName": "invoke",

        "fileName": "AccessLogValve.java",

        "lineNumber": 953,

        "className": "org.apache.catalina.valves.AccessLogValve",

        "nativeMethod": false

      },

      {

        "methodName": "invoke",

        "fileName": "RemoteIpValve.java",

        "lineNumber": 680,

        "className": "org.apache.catalina.valves.RemoteIpValve",

        "nativeMethod": false

      },

      {

        "methodName": "invoke",

        "fileName": "StandardEngineValve.java",

        "lineNumber": 118,

        "className": "org.apache.catalina.core.StandardEngineValve",

        "nativeMethod": false

      },

      {

        "methodName": "service",

        "fileName": "CoyoteAdapter.java",

        "lineNumber": 408,

        "className": "org.apache.catalina.connector.CoyoteAdapter",

        "nativeMethod": false

      },

      {

        "methodName": "process",

        "fileName": "AbstractHttp11Processor.java",

        "lineNumber": 1023,

        "className": "org.apache.coyote.http11.AbstractHttp11Processor",

        "nativeMethod": false

      },

      {

        "methodName": "process",

        "fileName": "AbstractProtocol.java",

        "lineNumber": 589,

        "className": "org.apache.coyote.AbstractProtocol$AbstractConnectionHandler",

        "nativeMethod": false

      },

      {

        "methodName": "run",

        "fileName": "JIoEndpoint.java",

        "lineNumber": 310,

        "className": "org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor",

        "nativeMethod": false

      },

      {

        "methodName": "runWorker",

        "fileName": "ThreadPoolExecutor.java",

        "lineNumber": 1110,

        "className": "java.util.concurrent.ThreadPoolExecutor",

        "nativeMethod": false

      },

      {

        "methodName": "run",

        "fileName": "ThreadPoolExecutor.java",

        "lineNumber": 603,

        "className": "java.util.concurrent.ThreadPoolExecutor$Worker",

        "nativeMethod": false

      },

      {

        "methodName": "run",

        "fileName": "Thread.java",

        "lineNumber": 679,

        "className": "java.lang.Thread",

        "nativeMethod": false

      }

    ],

    "authentication": null,

    "extraInformation": null,

    "message": "Invalid password change request",

    "localizedMessage": "Invalid password change request"

  },

  "trace": false

}

david.webe...@gmail.com

unread,
May 15, 2014, 8:40:10 AM5/15/14
to vcap...@cloudfoundry.org, david.webe...@gmail.com, Ryan Morgan
So I found the issue on this one. If I install the uaac client and set the password the request works and after that the cf pw works too.

Now is the question how to set the password over the API on initial user creation?

Filip Hanik

unread,
May 15, 2014, 9:34:52 AM5/15/14
to vcap...@cloudfoundry.org, David Weber
are you saying that a POST /Users doesn't set the password?


To unsubscribe from this group and stop receiving emails from it, send an email to vcap-dev+u...@cloudfoundry.org.

david.webe...@gmail.com

unread,
May 15, 2014, 9:48:40 AM5/15/14
to vcap...@cloudfoundry.org, David Weber
Thank for your reply Filip. I don't see it in the documentation (https://github.com/cloudfoundry/uaa/blob/master/docs/UAA-APIs.rst#create-a-user-post-users)?
The only option I'm aware of today is to set it over PUT /Users/{id}/password

Filip Hanik

unread,
May 15, 2014, 9:52:28 AM5/15/14
to vcap...@cloudfoundry.org, David Weber
yes, you are correct. so is PUT /Users/{id}/password not an option for you?

david.webe...@gmail.com

unread,
May 15, 2014, 10:00:07 AM5/15/14
to vcap...@cloudfoundry.org, David Weber
Sorry, it's my assumption was wrong. It's not documented but if I create a user over the uaac CLI I'm able to provide a password.

The body of the request looks like this:

{"userName":"my-user","password":"my-password","emails":[{"value":"my-email"}],"name":{"givenName":"my-name","familyName":"my-name"}

I couldn't test this because I get an access denied.

Reply all
Reply to author
Forward
0 new messages