从 Oauth 重定向 Uri 返回时,UWP OnNavigatedTo 未触发
UWP OnNavigatedTo is not firing when returned to from an Oauth redirect Uri
首先 - 我在此处 (https://github.com/googlesamples/oauth-apps-for-windows/tree/master/OAuthUniversalApp) 使用了 Google 提供的示例 UWP 应用程序。
我已经成功地使用了我自己的 clientID(设置记录在此处的答案中:How to create a custom OAuth 2.0 redirect_uri for Google provider, for UWP app?)。重定向回应用程序工作正常 - 并从示例中的 OnNavigatedTo() 方法触发。
然后我有一个 UWP 记录来自传感器的温度(最终将 运行 在 Raspberry Pi 上) - 但现在我 运行 在正常 Windows 10. 我已将示例中的 Oauth 位复制到我的项目中 - 并使用名称 "pw.oauth2" 在 Package.appxmanifest 中声明了协议(与示例相同,也与我匹配在 Google 开发者控制台中。
当我启动应用程序并单击 "Sign in with Google" 时,它会成功启动浏览器,请求 authorizations/sign-in,然后重定向回应用程序。我知道这部分工作正常,因为它将应用程序带回前台。但是,当返回到我的应用程序时,OnNavigatedTo() 方法没有触发。
我已经搜索并阅读了相关文档 - 但无法在我的应用程序中触发该方法。我觉得我错过了示例应用程序中设置的其他内容,但我没有在我的应用程序中配置。任何帮助或建议表示赞赏!
这是我的 XAML:
<Page
x:Class="CamIOT.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:CamIOT"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<RelativePanel>
<Button x:Name="button" HorizontalAlignment="Left" Margin="18,22,0,0" VerticalAlignment="Top" Width="206" Height="46" Click="oauth_Click"
BorderThickness="0" Padding="0" Background="Transparent">
<Image Source="Assets/btn_google_sign-in.png" Stretch="UniformToFill" HorizontalAlignment="Left" Width="206"/>
</Button>
<TextBox x:Name="textBoxOutput" TextWrapping="Wrap" Text=""
IsReadOnly="True" ScrollViewer.VerticalScrollBarVisibility="Auto" Margin="300,40,0,0" Height="731" Width="1000"/>
<ScrollViewer Margin="12,100,12,12">
<StackPanel>
<TextBlock TextWrapping="Wrap">
I2C Device Data
</TextBlock>
<TextBlock TextWrapping="Wrap" Margin="0,10,0,0">
Temperature and Humidity Data
</TextBlock>
<ScrollViewer HorizontalScrollMode="Auto" HorizontalScrollBarVisibility="Auto">
<Image Source="Assets/i2c_sample_bb.jpg" Stretch="None" Margin="2,10,2,0" />
</ScrollViewer>
<Button x:Name="StartStopButton" Content="Start" Margin="0,10,0,0" Click="{x:Bind StartStopScenario}"/>
<TextBlock x:Name="ScenarioControls" Visibility="Collapsed">
Current Temperature: <Run x:Name="CurrentTemp"/>
<LineBreak/>
Current Humidity: <Run x:Name="CurrentHumidity"/>
</TextBlock>
</StackPanel>
</ScrollViewer>
</RelativePanel>
</Grid>
</Page>
这是我的 OnNavigatedTo() 方法:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
Debug.WriteLine("OnNavigatedTo Fired!");
if (e.Parameter is Uri)
{
// Gets URI from navigation parameters.
Uri authorizationResponse = (Uri)e.Parameter;
string queryString = authorizationResponse.Query;
output("MainPage received authorizationResponse: " + authorizationResponse);
// Parses URI params into a dictionary
// ref:
Dictionary<string, string> queryStringParams =
queryString.Substring(1).Split('&')
.ToDictionary(c => c.Split('=')[0],
c => Uri.UnescapeDataString(c.Split('=')[1]));
if (queryStringParams.ContainsKey("error"))
{
output(String.Format("OAuth authorization error: {0}.", queryStringParams["error"]));
return;
}
if (!queryStringParams.ContainsKey("code")
|| !queryStringParams.ContainsKey("state"))
{
output("Malformed authorization response. " + queryString);
return;
}
// Gets the Authorization code & state
string code = queryStringParams["code"];
string incoming_state = queryStringParams["state"];
// Retrieves the expected 'state' value from local settings (saved when the request was made).
ApplicationDataContainer localSettings = ApplicationData.Current.LocalSettings;
string expected_state = (String)localSettings.Values["state"];
// Compares the receieved state to the expected value, to ensure that
// this app made the request which resulted in authorization
if (incoming_state != expected_state)
{
output(String.Format("Received request with invalid state ({0})", incoming_state));
return;
}
// Resets expected state value to avoid a replay attack.
localSettings.Values["state"] = null;
// Authorization Code is now ready to use!
output(Environment.NewLine + "Authorization code: " + code);
string code_verifier = (String)localSettings.Values["code_verifier"];
performCodeExchangeAsync(code, code_verifier);
}
else
{
Debug.WriteLine(e.Parameter);
}
}
关于我遗漏了什么以及为什么 OnNavigatedTo() 在示例应用程序中正常触发但在我的应用程序中却没有的任何想法?
提前感谢您的帮助!
最终我自己解决了这个问题 - 如果您正在关注 Google 为 UWP 提供的示例 Oauth 应用程序,来自问题中的 link - 您需要将下面的这个方法添加到您的 App.xaml.cs class - 当来自 Google 的重定向打开您的应用程序时将被调用。然后导航到您的主页,该主页触发 OnNavigatedTo() 方法中的代码。
protected override void OnActivated(IActivatedEventArgs args)
{
// When the app was activated by a Protocol (custom URI scheme), forwards
// the URI to the MainPage through a Navigate event.
if (args.Kind == ActivationKind.Protocol)
{
// Extracts the authorization response URI from the arguments.
ProtocolActivatedEventArgs protocolArgs = (ProtocolActivatedEventArgs)args;
Uri uri = protocolArgs.Uri;
Debug.WriteLine("Authorization Response: " + uri.AbsoluteUri);
// Gets the current frame, making one if needed.
var frame = Window.Current.Content as Frame;
if (frame == null)
frame = new Frame();
// Opens the URI for "navigation" (handling) on the MainPage.
frame.Navigate(typeof(MainPage), uri);
Window.Current.Content = frame;
Window.Current.Activate();
}
}
首先 - 我在此处 (https://github.com/googlesamples/oauth-apps-for-windows/tree/master/OAuthUniversalApp) 使用了 Google 提供的示例 UWP 应用程序。
我已经成功地使用了我自己的 clientID(设置记录在此处的答案中:How to create a custom OAuth 2.0 redirect_uri for Google provider, for UWP app?)。重定向回应用程序工作正常 - 并从示例中的 OnNavigatedTo() 方法触发。
然后我有一个 UWP 记录来自传感器的温度(最终将 运行 在 Raspberry Pi 上) - 但现在我 运行 在正常 Windows 10. 我已将示例中的 Oauth 位复制到我的项目中 - 并使用名称 "pw.oauth2" 在 Package.appxmanifest 中声明了协议(与示例相同,也与我匹配在 Google 开发者控制台中。
当我启动应用程序并单击 "Sign in with Google" 时,它会成功启动浏览器,请求 authorizations/sign-in,然后重定向回应用程序。我知道这部分工作正常,因为它将应用程序带回前台。但是,当返回到我的应用程序时,OnNavigatedTo() 方法没有触发。
我已经搜索并阅读了相关文档 - 但无法在我的应用程序中触发该方法。我觉得我错过了示例应用程序中设置的其他内容,但我没有在我的应用程序中配置。任何帮助或建议表示赞赏!
这是我的 XAML:
<Page
x:Class="CamIOT.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:CamIOT"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<RelativePanel>
<Button x:Name="button" HorizontalAlignment="Left" Margin="18,22,0,0" VerticalAlignment="Top" Width="206" Height="46" Click="oauth_Click"
BorderThickness="0" Padding="0" Background="Transparent">
<Image Source="Assets/btn_google_sign-in.png" Stretch="UniformToFill" HorizontalAlignment="Left" Width="206"/>
</Button>
<TextBox x:Name="textBoxOutput" TextWrapping="Wrap" Text=""
IsReadOnly="True" ScrollViewer.VerticalScrollBarVisibility="Auto" Margin="300,40,0,0" Height="731" Width="1000"/>
<ScrollViewer Margin="12,100,12,12">
<StackPanel>
<TextBlock TextWrapping="Wrap">
I2C Device Data
</TextBlock>
<TextBlock TextWrapping="Wrap" Margin="0,10,0,0">
Temperature and Humidity Data
</TextBlock>
<ScrollViewer HorizontalScrollMode="Auto" HorizontalScrollBarVisibility="Auto">
<Image Source="Assets/i2c_sample_bb.jpg" Stretch="None" Margin="2,10,2,0" />
</ScrollViewer>
<Button x:Name="StartStopButton" Content="Start" Margin="0,10,0,0" Click="{x:Bind StartStopScenario}"/>
<TextBlock x:Name="ScenarioControls" Visibility="Collapsed">
Current Temperature: <Run x:Name="CurrentTemp"/>
<LineBreak/>
Current Humidity: <Run x:Name="CurrentHumidity"/>
</TextBlock>
</StackPanel>
</ScrollViewer>
</RelativePanel>
</Grid>
</Page>
这是我的 OnNavigatedTo() 方法:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
Debug.WriteLine("OnNavigatedTo Fired!");
if (e.Parameter is Uri)
{
// Gets URI from navigation parameters.
Uri authorizationResponse = (Uri)e.Parameter;
string queryString = authorizationResponse.Query;
output("MainPage received authorizationResponse: " + authorizationResponse);
// Parses URI params into a dictionary
// ref:
Dictionary<string, string> queryStringParams =
queryString.Substring(1).Split('&')
.ToDictionary(c => c.Split('=')[0],
c => Uri.UnescapeDataString(c.Split('=')[1]));
if (queryStringParams.ContainsKey("error"))
{
output(String.Format("OAuth authorization error: {0}.", queryStringParams["error"]));
return;
}
if (!queryStringParams.ContainsKey("code")
|| !queryStringParams.ContainsKey("state"))
{
output("Malformed authorization response. " + queryString);
return;
}
// Gets the Authorization code & state
string code = queryStringParams["code"];
string incoming_state = queryStringParams["state"];
// Retrieves the expected 'state' value from local settings (saved when the request was made).
ApplicationDataContainer localSettings = ApplicationData.Current.LocalSettings;
string expected_state = (String)localSettings.Values["state"];
// Compares the receieved state to the expected value, to ensure that
// this app made the request which resulted in authorization
if (incoming_state != expected_state)
{
output(String.Format("Received request with invalid state ({0})", incoming_state));
return;
}
// Resets expected state value to avoid a replay attack.
localSettings.Values["state"] = null;
// Authorization Code is now ready to use!
output(Environment.NewLine + "Authorization code: " + code);
string code_verifier = (String)localSettings.Values["code_verifier"];
performCodeExchangeAsync(code, code_verifier);
}
else
{
Debug.WriteLine(e.Parameter);
}
}
关于我遗漏了什么以及为什么 OnNavigatedTo() 在示例应用程序中正常触发但在我的应用程序中却没有的任何想法?
提前感谢您的帮助!
最终我自己解决了这个问题 - 如果您正在关注 Google 为 UWP 提供的示例 Oauth 应用程序,来自问题中的 link - 您需要将下面的这个方法添加到您的 App.xaml.cs class - 当来自 Google 的重定向打开您的应用程序时将被调用。然后导航到您的主页,该主页触发 OnNavigatedTo() 方法中的代码。
protected override void OnActivated(IActivatedEventArgs args)
{
// When the app was activated by a Protocol (custom URI scheme), forwards
// the URI to the MainPage through a Navigate event.
if (args.Kind == ActivationKind.Protocol)
{
// Extracts the authorization response URI from the arguments.
ProtocolActivatedEventArgs protocolArgs = (ProtocolActivatedEventArgs)args;
Uri uri = protocolArgs.Uri;
Debug.WriteLine("Authorization Response: " + uri.AbsoluteUri);
// Gets the current frame, making one if needed.
var frame = Window.Current.Content as Frame;
if (frame == null)
frame = new Frame();
// Opens the URI for "navigation" (handling) on the MainPage.
frame.Navigate(typeof(MainPage), uri);
Window.Current.Content = frame;
Window.Current.Activate();
}
}