Answered "Relay security token is required."

  • Tuesday, August 09, 2011 6:45 PM
     
     

    I have a problem getting ACS to work with my Silverlight application. (For simplicity, I’ve used a normal console app to illustrate what I’m trying to do. But I am using basicHttpBinding on the client side.)

    Everything works fine calling a REST service:

    string realm = "https://[ns].servicebus.appfabriclabs.com/RESTService.svc/";       

    WebClient client = new WebClient();

     

    string token = GetTokenFromACS(realm);

    string headerValue = string.Format("WRAP access_token=\"{0}\"", token);

    client.Headers.Add("Authorization", headerValue);

    Stream stream = client.OpenRead(realm+"Customers");

     

    However, when trying to call a regular WCF Service (basicHttpRelayBinding on server side), I get a “Relay security token is required.” exception. I’ve tried adding the header using WebOperationContext:

    Service1Client client = new Service1Client("Cloud");

    using (new OperationContextScope(client.InnerChannel))

    {

        string token = GetTokenFromACS(realm); // Same token as when calling the REST service

        string tokenHeader = string.Format("WRAP access_token=\"{0}\"", token);

        WebOperationContext.Current.OutgoingRequest.Headers.Add("Authorization",tokenHeader);

        string ret = client.GetData(123); // “Relay security token is required” Exception

    }

     

    Trace:

    POST /Service1.svc HTTP/1.1

    Content-Type: text/xml; charset=utf-8

    Authorization: WRAP access_token="net.windows.servicebus.action=Listen%2cSend&http%3a%2f%2fschemas.microsoft.com%2faccesscontrolservice%2f2010%2f07%2fclaims%2fidentityprovider=https%3a%2f%2fbLogical-sb.accesscontrol.appfabriclabs.com%2f&Audience=http%3a%2f%2fblogical.servicebus.appfabriclabs.com%2f&ExpiresOn=1312914390&Issuer=https%3a%2f%2fblogical-sb.accesscontrol.appfabriclabs.com%2f&HMACSHA256=1K7gscu5FxCGWlFLWhHEGj5359jyH%2bW6DFOe8MQNnMM%3d"

     

    I’ve also tried using OperationContext to add the envelope header…

    Service1Client client = new Service1Client("Cloud");

    using (new OperationContextScope(client.InnerChannel))

    {

        string token = GetTokenFromACS(realm); // Same token as when calling the REST service

        string tokenHeader = string.Format("WRAP access_token=\"{0}\"", token);

        OperationContext.Current.OutgoingMessageHeaders.Add(new ACSHeader(tokenHeader));

        string ret = client.GetData(123); // “Relay security token is required” Exception

    }

     

    Trace:

    POST /Service1.svc HTTP/1.1

    Content-Type: text/xml; charset=utf-8

    VsDebuggerCausalityData: uIDPozOWNE8J2a9CrpfKtndoEv8AAAAAfqwOWuc5SkaKtNffqNBLOoINjcIBjNRGkRGb0TvcRtkACQAA

    SOAPAction: "http://tempuri.org/IService1/GetData"

    Host: localhost:8789

    Content-Length: 711

    Expect: 100-continue

    Accept-Encoding: gzip, deflate

    Connection: Keep-Alive

     

    <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Header><Authorization xmlns="http://bLogical.se/ACSHeader"><Key>WRAP access_token="net.windows.servicebus.action=Listen%2cSend&amp;http%3a%2f%2fschemas.microsoft.com%2faccesscontrolservice%2f2010%2f07%2fclaims%2fidentityprovider=https%3a%2f%2fbLogical-sb.accesscontrol.appfabriclabs.com%2f&amp;Audience=http%3a%2f%2fblogical.servicebus.appfabriclabs.com%2f&amp;ExpiresOn=1312915757&amp;Issuer=https%3a%2f%2fblogical-sb.accesscontrol.appfabriclabs.com%2f&amp;HMACSHA256=y3ZuRQwVEVpO2g6lUtYcjwcda6A5eOJi5kmopyjhZdk%3d"</Key></Authorization></s:Header><s:Body><GetData xmlns="http://tempuri.org/"><value>123</value></GetData></s:Body></s:Envelope>

     

    So bottom-line, how do I add a header to the WCF call when I can only using basicHttpBinding? Is it supposed to be in the header of the envelope header? If in the envelope header, what header value and jey should I use?

    Thanks

    //Mikael


    If this answers your question, please use the "Answer" button to say so... Mikael - http://blogical.se/blogs/mikael

All Replies

  • Thursday, August 11, 2011 3:02 AM
     
     Answered Has Code

    Mikael,

    It looks like you are SOOOO CLOSE! Not too sure where AcsHeader is coming from, but the implementation should look like this.

    After getting the token, store it like so:

     

          _token = HttpUtility.UrlDecode(reader.ReadToEnd().Split('=')[1]).Replace("&wrap_access_token_expires_in"string.Empty);
     
          var bytes = Encoding.UTF8.GetBytes(_token);
          _token = Convert.ToBase64String(bytes);
    

    Then insert as you show above with an AcsHeader implementation that looks exactly like this:

    public class AcsHeader : MessageHeader
    {
      private string _token;
      public AcsHeader(string token)
      {
        _token = token;
      }
      public override string Name
      {
        get { return "RelayAccessToken"; }
      }
     
      public override string Namespace
      {
        get { return "http://schemas.microsoft.com/netservices/2009/05/servicebus/connect"; }
      }
     
      protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion)
      {
        writer.WriteStartElement("wsse""BinarySecurityToken""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
        writer.WriteAttributeString("wsu""Id""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd",
                                    string.Format("uuid:{0}"Guid.NewGuid().ToString("D")));
        writer.WriteAttributeString("ValueType""http://schemas.xmlsoap.org/ws/2009/11/swt-token-profile-1.0");
        writer.WriteAttributeString("EncodingType""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary");
        writer.WriteString(_token);
        writer.WriteEndElement();
      }
    }

    Scott Seely