How to: Specify Client Credentials for a Data Service Request (WCF Data Services)
Important
WCF Data Services has been deprecated and will no longer be available for download from the Microsoft Download Center. WCF Data Services supported earlier versions of the Microsoft OData (V1-V3) protocol only and has not been under active development. OData V1-V3 has been superseded by OData V4, which is an industry standard published by OASIS and ratified by ISO. OData V4 is supported through the OData V4 compliant core libraries available at Microsoft.OData.Core. Support documentation is available at OData.Net, and the OData V4 service libraries are available at Microsoft.AspNetCore.OData.
RESTier is the successor to WCF Data Services. RESTier helps you bootstrap a standardized, queryable, HTTP-based REST interface in minutes. Like WCF Data Services before it, Restier provides simple and straightforward ways to shape queries and intercept submissions before and after they hit the database. And like Web API + OData, you still have the flexibility to add your own custom queries and actions with techniques you're already familiar with.
By default, the client library does not supply credentials when sending a request to an OData service. However, you can specify that credentials be sent to authenticate requests to the data service by supplying a NetworkCredential for the Credentials property of the DataServiceContext. For more information, see Securing WCF Data Services. The example in this topic shows how to explicitly provide credentials that are used by the WCF Data Services client when requesting data from the data service.
The example in this topic uses the Northwind sample data service and autogenerated client data service classes. This service and the client data classes are created when you complete the WCF Data Services quickstart. You can also use the Northwind sample data service that is published on the OData Web site; this sample data service is read-only and attempting to save changes returns an error. The sample data services on the OData Web site allow anonymous authentication.
Example
The following example is from the code-behind page for an Extensible Application Markup Language (XAML) file that is the main page of the Windows Presentation Framework application. This example displays a LoginWindow
instance to collect authentication credentials from the user, and then uses these credentials when making a request to the data service.
using System;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Security;
using NorthwindClient.Northwind;
using System.Data.Services.Client;
namespace NorthwindClient
{
public partial class ClientCredentials : Window
{
// Create the binding collections and the data service context.
private DataServiceCollection<Customer> binding;
NorthwindEntities context;
CollectionViewSource customerAddressViewSource;
// Instantiate the service URI and credentials.
Uri serviceUri = new Uri("http://localhost:12345/Northwind.svc/");
NetworkCredential credentials = new NetworkCredential();
public ClientCredentials()
{
InitializeComponent();
}
private void ClientCredentials_Loaded(object sender, RoutedEventArgs e)
{
string userName = string.Empty;
string domain = string.Empty;
SecureString password = new SecureString();
// Get credentials for authentication.
LoginWindow login = new LoginWindow();
login.ShowDialog();
if (login.DialogResult == true
&& login.userNameBox.Text != string.Empty
&& login.passwordBox.SecurePassword.Length != 0)
{
// Instantiate the context.
context =
new NorthwindEntities(serviceUri);
// Get the user name and domain from the login.
string[] qualifiedUserName = login.userNameBox.Text.Split(new char[] { '\\' });
if (qualifiedUserName.Length == 2)
{
domain = qualifiedUserName[0];
userName = qualifiedUserName[1];
}
else
{
userName = login.userNameBox.Text;
}
password = login.passwordBox.SecurePassword;
// Set the client authentication credentials.
context.Credentials =
new NetworkCredential(userName, password, domain);
// Define an anonymous LINQ query that returns a collection of Customer types.
var query = from c in context.Customers
where c.Country == "Germany"
select c;
try
{
// Instantiate the binding collection, which executes the query.
binding = new DataServiceCollection<Customer>(query);
while (binding.Continuation != null)
{
// Continue to execute the query until all pages are loaded.
binding.Load(context.Execute<Customer>(binding.Continuation.NextLinkUri));
}
// Assign the binding collection to the CollectionViewSource.
customerAddressViewSource =
(CollectionViewSource)this.Resources["customerViewSource"];
customerAddressViewSource.Source = binding;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
else if (login.DialogResult == false)
{
MessageBox.Show("Login cancelled.");
}
}
}
}
Imports NorthwindClient.Northwind
Imports System.Data.Services.Client
Imports System.Windows.Data
Imports System.Net
Imports System.Windows
Imports System.Security
Partial Public Class ClientCredentials
Inherits Window
' Create the binding collections and the data service context.
Private binding As DataServiceCollection(Of Customer)
Private context As NorthwindEntities
Private customerAddressViewSource As CollectionViewSource
' Instantiate the service URI and credentials.
Dim serviceUri As Uri = New Uri("http://localhost:54321/Northwind.svc/")
Private credentials As NetworkCredential = New NetworkCredential()
Public Sub Main()
InitializeComponent()
End Sub
Private Sub ClientCredentials_Loaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
Dim userName = String.Empty
Dim domain = String.Empty
Dim password = New SecureString()
' Get credentials for authentication.
Dim login As New LoginWindow()
login.ShowDialog()
If login.DialogResult = True _
AndAlso Not login.userNameBox.Text Is String.Empty _
AndAlso login.passwordBox.SecurePassword.Length <> 0 Then
' Instantiate the context.
context = New NorthwindEntities(serviceUri)
' Get the user name and domain from the login.
Dim qualifiedUserName As String() = login.userNameBox.Text.Split(New [Char]() {"\"c})
If qualifiedUserName.Length = 2 Then
domain = qualifiedUserName(0)
userName = qualifiedUserName(1)
Else
userName = login.userNameBox.Text
End If
password = login.passwordBox.SecurePassword
' Set the client authentication credentials.
context.Credentials = _
New NetworkCredential(userName, password, domain)
' Define an anonymous LINQ query that returns a collection of Customer types.
Dim query = From c In context.Customers
Where c.Country = "Germany"
Select c
Try
' Instantiate the binding collection, which executes the query.
binding = New DataServiceCollection(Of Customer)(query)
' Load result pages into the binding collection.
While Not binding.Continuation Is Nothing
' Continue to execute the query until all pages are loaded.
binding.Load(context.Execute(Of Customer)(binding.Continuation.NextLinkUri))
End While
' Assign the binding collection to the CollectionViewSource.
customerAddressViewSource = _
CType(Me.Resources("customerViewSource"), CollectionViewSource)
customerAddressViewSource.Source = binding
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
ElseIf login.DialogResult = False Then
MessageBox.Show("Login cancelled.")
End If
End Sub
End Class
Example
The following XAML defines the main page of the WPF application.
<Window x:Class="ClientCredentials"
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="312" d:DesignWidth="577"
Loaded="ClientCredentials_Loaded">
<Window.Resources>
<CollectionViewSource x:Key="customerViewSource" />
</Window.Resources>
<Grid x:Name="LayoutRoot" Background="White" DataContext="" Height="312" Width="577"
VerticalAlignment="Top" HorizontalAlignment="Left">
<Grid.RowDefinitions>
<RowDefinition Height="203*" />
<RowDefinition Height="119*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="336*" />
</Grid.ColumnDefinitions>
<DataGrid AutoGenerateColumns="False" Height="213" HorizontalAlignment="Left"
ItemsSource="{Binding Source={StaticResource customerViewSource}}"
Name="customerDataGrid" RowDetailsVisibilityMode="VisibleWhenSelected"
VerticalAlignment="Top" Width="553" Margin="12,44,0,0"
Grid.RowSpan="2" Grid.ColumnSpan="1">
<DataGrid.Columns>
<DataGridTextColumn x:Name="customerIDColumn" Binding="{Binding Path=CustomerID}"
Header="Customer" Width="80" />
<DataGridTextColumn x:Name="addressColumn" Binding="{Binding Path=Address}"
Header="Address" Width="180" />
<DataGridTextColumn x:Name="cityColumn" Binding="{Binding Path=City}"
Header="City" Width="120" />
<DataGridTextColumn x:Name="countryColumn" Binding="{Binding Path=Country}"
Header="Country" Width="80" />
<DataGridTextColumn x:Name="postalCodeColumn" Binding="{Binding Path=PostalCode}"
Header="Postal Code" Width="90" />
<DataGridTextColumn Binding="{Binding Path=CompanyName}" Header="CompanyName" />
<DataGridTextColumn Binding="{Binding Path=ContactName}" Header="ContactName" />
<DataGridTextColumn Binding="{Binding Path=Phone}" Header="Phone" />
</DataGrid.Columns>
</DataGrid>
<Label Grid.Row="0" Grid.Column="0" Height="26" HorizontalAlignment="Left" Margin="16,12,0,0"
Name="serviceUriLabel" VerticalAlignment="Top" Width="550" />
</Grid>
</Window>
Example
The following example is from the code-behind page for the window that is used to collect the authentication credentials from the user before making a request to the data service.
using System;
using System.Windows;
using System.Windows.Controls;
using System.ComponentModel;
namespace NorthwindClient
{
public partial class LoginWindow : Window
{
public LoginWindow()
{
InitializeComponent();
}
private void OKButton_Click(object sender, RoutedEventArgs e)
{
this.DialogResult = true;
e.Handled = true;
}
private void CancelButton_Click(object sender, RoutedEventArgs e)
{
this.DialogResult = false;
e.Handled = true;
}
private void LoginWindow_Closing(object sender, CancelEventArgs e)
{
if (this.DialogResult == true &&
(this.userNameBox.Text == string.Empty || this.passwordBox.SecurePassword.Length == 0))
{
e.Cancel = true;
MessageBox.Show("Please enter name and password or click Cancel.");
}
}
}
}
Imports System.ComponentModel
Imports System.Windows
Imports System.Security
Partial Public Class LoginWindow
Inherits Window
Public Sub New()
InitializeComponent()
End Sub
Private Sub OKButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) Handles OKButton.Click
Me.DialogResult = True
e.Handled = True
End Sub
Private Sub CancelButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) Handles CancelButton.Click
Me.DialogResult = False
e.Handled = True
End Sub
Private Sub LoginWindow_Closing(ByVal sender As System.Object, ByVal e As CancelEventArgs)
If Me.DialogResult = True AndAlso _
(Me.userNameBox.Text = String.Empty OrElse Me.passwordBox.SecurePassword.Length = 0) Then
e.Cancel = True
MessageBox.Show("Please enter name and password or click Cancel.")
End If
End Sub
End Class
Example
The following XAML defines the login of the WPF application.
<Window x:Class="LoginWindow"
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
Width="400" Height="200"
Title="LoginWindow" xmlns:sdk="https://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" Closing="LoginWindow_Closing">
<StackPanel Name="LayoutRoot" Orientation="Vertical" VerticalAlignment="Top">
<StackPanel Orientation="Horizontal">
<TextBlock Height="25" HorizontalAlignment="Left" Margin="10,20,0,0" Name="userNameLabel" VerticalAlignment="Top"
Width="80" Text="User name:"/>
<TextBox Height="23" HorizontalAlignment="Left" Margin="10,20,0,0" Name="userNameBox" VerticalAlignment="Top"
Width="150" Text="DOMAIN\login"/>
</StackPanel>
<StackPanel Orientation="Horizontal" VerticalAlignment="Top">
<TextBlock Height="25" HorizontalAlignment="Left" Margin="10,20,0,0" Name="pwdLabel" Width="80" Text="Password:"/>
<PasswordBox Height="23" HorizontalAlignment="Left" Margin="10,20,0,0" Name="passwordBox" Width="150" />
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Height="80" VerticalAlignment="Top">
<Button x:Name="CancelButton" Content="Cancel" Click="CancelButton_Click" Width="75" Height="23"
HorizontalAlignment="Right" Margin="8" IsCancel="True" />
<Button x:Name="OKButton" Content="OK" Click="OKButton_Click" Width="75" Height="23"
HorizontalAlignment="Right" Margin="8" IsDefault="True" />
</StackPanel>
</StackPanel>
</Window>
.NET Framework Security
The following security considerations apply to the example in this topic:
To verify that the credentials supplied in this sample work, the Northwind data service must use an authentication scheme other than anonymous access. Otherwise, the Web site hosting the data service will not request credentials.
User credentials should only be requested during execution and should not be cached. Credentials must always be stored securely.
Data sent with Basic and Digest Authentication is not encrypted, so the data can be seen by an adversary. Additionally, basic authentication credentials (user name and password) are sent in cleartext and can be intercepted.
For more information, see Securing WCF Data Services.