Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
396 views
in Technique[技术] by (71.8m points)

c# - Does the WebAuthenticationBroker work in Windows 8 Metro App post Release Candidate

SOLUTION My working solution can be found in the answer or in my update two.

1) Now make sure, for testing on localhost, that you have setup windows firewalls for inbound on the localhost port. Port forwarding on the router if you have one.

2) Then you need to tell IIS Express that its okay that the request comes from outsite the localhost: Find DocumentsIISExpressconfig and edit applicationhost.config. Find your site in the list and remove the localhost from the binding.

        <site name="S-Innovations.TrafficTheory.Web2" id="1591449597">
            <application path="/" applicationPool="Clr4IntegratedAppPool">
                <virtualDirectory path="/" physicalPath="G:DocumentsVisual Studio 2012ProjectsS-Innovations.TrafficTheoryS-Innovations.TrafficTheory.Web2" />
            </application>
            <bindings>
                <binding protocol="http" bindingInformation="*:909090:localhost" />
            </bindings>
        </site>

2a) ISS need to run as administrator, running visual studio as administrator also starts iss as admin...

3) Locate your ip, www.myip.com and change the ACS return uri to : http://90.90.90.90:909090/api/federation/

4) change the webbroker to use your ip also:

        WebAuthenticationResult webAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync(
WebAuthenticationOptions.None,
new Uri("https://traffictheory.accesscontrol.windows.net:443/v2/wsfederation?wa=wsignin1.0&wtrealm=http%3a%2f%2flocalhost%3a48451%2f"),
new Uri("http://99.99.99.99:909090/api/federation/end"));

Everything worked for me like this. I got a hello world passed on to my metro app as the token.

Problem

I have set up a WCF Service and a Metro App. The WCF service is set up to authenticate using Azure ACS.

I made a Console Application that works with the WebService and ACS:

            static void Main(string[] args)
        {
            try
            {

                // First start the web project, then the client
                WebClient client = new WebClient();
                var token = RetrieveACSToken();
                client.Headers.Add("Authorization", token);
                client.Headers.Add("Content-type", "text/xml");
                var url = new Uri("http://traffictheory.azurewebsites.net/UserService.svc/Users");
                //var url = new Uri("http://localhost:4000/UserService.svc/Users");//
                Stream stream = client.OpenRead(url);
                StreamReader reader = new StreamReader(stream);
                String response = reader.ReadToEnd();
                Console.Write(response);

            }
            catch (Exception ex)
            {
                Console.Write(ex.Message);
            }
              Console.ReadLine();
        }

        private static string RetrieveACSToken()
        {
            var acsHostName     = ConfigurationManager.AppSettings.Get("ACSHostName");
            var acsNamespace    = ConfigurationManager.AppSettings.Get("ACSNamespace");
            var username        = ConfigurationManager.AppSettings.Get("ServiceIdentityUserName");
            var password        = ConfigurationManager.AppSettings.Get("ServiceIdentityCredentialPassword");
            var scope = "http://traffictheory.azurewebsites.net/"; 
            //var scope = "http://localhost:4000/";// 
            // request a token from ACS
            WebClient client = new WebClient();
            client.BaseAddress = string.Format("https://{0}.{1}", acsNamespace, acsHostName);
            NameValueCollection values = new
            NameValueCollection();
            values.Add("wrap_name", username);
            values.Add("wrap_password", password);
            values.Add("wrap_scope", scope);
            byte[] responseBytes =
            client.UploadValues("WRAPv0.9", "POST", values);
            string response =
            Encoding.UTF8.GetString(responseBytes);
            string token = response
            .Split('&')
            .Single(value =>
            value.StartsWith("wrap_access_token=",
            StringComparison.OrdinalIgnoreCase))
            .Split('=')[1];
            var decodedToken = string.Format("WRAP access_token="{0}"", HttpUtility.UrlDecode(token));
            return decodedToken;
        }

I face two problems now when i want to use it from my Metro App. First one is unrelated to the service and is about the WebAuthenticationBroker.

1) When i use

WebAuthenticationResult webAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync(
                WebAuthenticationOptions.None,
                new Uri("https://s-innovations.accesscontrol.windows.net:443/v2/wsfederation?wa=wsignin1.0&wtrealm=http%3a%2f%2ftraffictheory.azurewebsites.net%2f"),
                new Uri("https://s-innovations.accesscontrol.windows.net")
            );

I am able to log in using, LiveID, Facebook ect. Not google because ACS dont include the ID correctly. But I dont get any kind of token back or Claims. I only get:

https://s-innovations.accesscontrol.windows.net/v2/wsfederation?wa=wsignin1.0 https://s-innovations.accesscontrol.windows.net/v2/facebook?cx=cHI9d3NmZWRlcmF0aW9uJn...cmFmZmljdGhlb3J5LmF6dXJld2Vic2l0ZXMubmV0JTJmJmlwPUZhY2Vib29rLTM1NTk5MjQ2NzgxNzc5OQ2&code=AQDagvqoXQ.......

How do I get the claims like in the end of this movie: http://channel9.msdn.com/Events/BUILD/BUILD2011/SAC-858T His app works!

2) The console app shown above get authenticated and get the token to pass to the service when calling the API, how do i get this token from within the metro app.

UPDATE

I created the controller as suggested:

[HttpPost]
public ActionResult End()
{
    return Json("Hello World");
}

I have put in a break point to see if it get it. No hit yet.

    WebAuthenticationResult webAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync(
WebAuthenticationOptions.None,
new Uri("https://traffictheory.accesscontrol.windows.net:443/v2/wsfederation?wa=wsignin1.0&wtrealm=http%3a%2f%2flocalhost%3a48451%2f"),
new Uri("http://localhost:909090/Federation/End"));

On my Relying Party Application i ahave

Realm http://localhost:909090/
Return Url: Nothing  (have tried http://localhost:909090/Federation/End )

The response data contains : http://localhost:909090/Federation/End right now.

UPDATE 2

I also tried with an api controller as you shown in another post:

public class FederationController : ApiController {

public HttpResponseMessage Post()
{
    var response = this.Request.CreateResponse(HttpStatusCode.Redirect);
    response.Headers.Add("Location", "/api/federation/end?acsToken=" + ExtractBootstrapToken());

    return response;
}
public string Get()
{
    return "hello world";
}
protected virtual string ExtractBootstrapToken()
{
    return "Hello World";
}

}

Now the login screen just hang and ends with a service you looking for is not ready right now (or something like that).

acs return url http://localhost:48451/api/Federation

        WebAuthenticationResult webAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync(
WebAuthenticationOptions.None,
new Uri("https://traffictheory.accesscontrol.windows.net:443/v2/wsfederation?wa=wsignin1.0&wtrealm=http%3a%2f%2flocalhost%3a909090%2f"),
new Uri("http://localhost:909090/api/federation/end"));
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The WebAuthenticationBroker simply keeps browsing until the next requested page is the one specified by the callbackUri parameter. At that point it returns the final URL to you so if you want to get anything back it needs to be encoded in that URL.

In the ACS control panel for the relying party you need to specify a return url that is somewhere on your site. For example https://traffictheory.azurewebsites.net/federationcallback. Then create a controller to handle accept a post to that URL. The post will have a form field wresult which is some xml that will contain the token returned from ACS.

You can then send the token back to the WebAuthenticationBroker by redirecting to https://traffictheory.azurewebsites.net/federationcallback/end?token={whatever you want to return}

You would then need to change the usage of the authentication broker to the following:

var webAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync(
            WebAuthenticationOptions.None,
            new Uri("https://s-innovations.accesscontrol.windows.net:443/v2/wsfederation?wa=wsignin1.0&wtrealm=http%3a%2f%2ftraffictheory.azurewebsites.net%2f"),
            new Uri("https://traffictheory.azurewebsites.net/federationcallback/end")
        );

// The data you returned
var token = authenticateResult.ResponseData.Substring(authenticateResult.ResponseData.IndexOf("token=", StringComparison.Ordinal) + 6);

My controller for handling the authentication callback post looks like this.

public class FederationcallbackController : ApiController
{
    public HttpResponseMessage Post()
    {
        var response = this.Request.CreateResponse(HttpStatusCode.Redirect);
        response.Headers.Add("Location", "/api/federationcallback/end?acsToken=" + ExtractBootstrapToken());

        return response;
    }

    protected virtual string ExtractBootstrapToken()
    {
        return HttpContext.Current.User.BootstrapToken();
    }
}

The BootstrapToken() extenion method is part of the wif.swt NuGet package. By default WIF doesn't save anything to the bootstrap token property you need to enable it by including the saveBootstrapTokens="true" attribute on the <service> element under <microsoft.identityModel> in your web.config. Mine looks like this:

<microsoft.identityModel>
    <service saveBootstrapTokens="true">
        <audienceUris>
            <add value="http://localhost:3949/" />
        </audienceUris>
        <federatedAuthentication>
            <wsFederation passiveRedirectEnabled="true" issuer="https://xyz.accesscontrol.windows.net/v2/wsfederation" realm="http://localhost:3949/" reply="http://localhost:3949/" requireHttps="false" />
            <cookieHandler requireSsl="false" path="/" />
        </federatedAuthentication>
        <issuerNameRegistry type="Microsoft.IdentityModel.Swt.SwtIssuerNameRegistry, Wif.Swt">
            <trustedIssuers>
                <add name="https://readify.accesscontrol.windows.net/" thumbprint="{thumbprint}" />
            </trustedIssuers>
        </issuerNameRegistry>
        <securityTokenHandlers>
            <add type="Microsoft.IdentityModel.Swt.SwtSecurityTokenHandler, Wif.Swt" />
        </securityTokenHandlers>
        <issuerTokenResolver type="Microsoft.IdentityModel.Swt.SwtIssuerTokenResolver, Wif.Swt" />
    </service>
</microsoft.identityModel>

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...